From 88ad3d1df57c553aeef49e3d33e639001ab9558a Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 14 May 2024 15:21:25 +0200 Subject: [PATCH 1/4] Add missing release notes --- RELEASE_NOTES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index be335bc1..4e3a7895 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,9 @@ ### 5.4.10 - [OpenVR] changed GL texture submit to 2 textures (previously side by side, issue with Quest 3) +- [GL] Improved querying of supported sample counts +- [GL] Fixed double disposal of Context +- [GLFW] Fixed OpenTK context interop +- [Vulkan] Fixed conservative raster validation error ### 5.4.9 - [LodRenderer] Handle exceptions in background threads From f5d7de2288297652140888652362fffec4bc428a Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 14 May 2024 17:10:51 +0200 Subject: [PATCH 2/4] [IndexedGeometry] Add overload Clone() for deep copy --- .../Geometry/IndexedGeometry.fs | 33 ++++++++++++--- .../Aardvark.Rendering.Tests.fsproj | 1 + .../Tests/Other/IndexedGeometryTests.fs | 42 +++++++++++++++++++ 3 files changed, 70 insertions(+), 6 deletions(-) create mode 100644 src/Tests/Aardvark.Rendering.Tests/Tests/Other/IndexedGeometryTests.fs diff --git a/src/Aardvark.Rendering/Geometry/IndexedGeometry.fs b/src/Aardvark.Rendering/Geometry/IndexedGeometry.fs index 2822c3c3..f8a16ae7 100644 --- a/src/Aardvark.Rendering/Geometry/IndexedGeometry.fs +++ b/src/Aardvark.Rendering/Geometry/IndexedGeometry.fs @@ -175,13 +175,34 @@ type IndexedGeometry = member x.FaceCount = IndexedGeometryMode.faceCount x.Mode x.FaceVertexCount + ///Creates a copy. + ///If true, the index and attribute arrays are reused instead of being copied. + member x.Clone(shallowCopy: bool) = + let indices = + if isNull x.IndexArray then + null + else + if shallowCopy then x.IndexArray + else x.IndexArray.Copy() + + let indexedAttributes = + if isNull x.IndexedAttributes then + null + else + let d = SymbolDict(initialCapacity = x.IndexedAttributes.Count) + for (KeyValue(sem, attr)) in x.IndexedAttributes do + d.[sem] <- if shallowCopy then attr else attr.Copy() + d + + let singleAttributes = + if isNull x.SingleAttributes then null + else x.SingleAttributes.Copy() + + IndexedGeometry(x.Mode, indices, indexedAttributes, singleAttributes) + + /// Creates a copy. The copy is shallow as the index and attribute arrays are reused instead of being copied. member x.Clone() = - IndexedGeometry( - x.Mode, - x.IndexArray, - (if isNull x.IndexedAttributes then null else x.IndexedAttributes.Copy()), - (if isNull x.SingleAttributes then null else x.SingleAttributes.Copy()) - ) + x.Clone(true) member x.ToIndexed() = if isNull x.IndexArray then diff --git a/src/Tests/Aardvark.Rendering.Tests/Aardvark.Rendering.Tests.fsproj b/src/Tests/Aardvark.Rendering.Tests/Aardvark.Rendering.Tests.fsproj index 7e8332da..3125add4 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/IndexedGeometryTests.fs b/src/Tests/Aardvark.Rendering.Tests/Tests/Other/IndexedGeometryTests.fs new file mode 100644 index 00000000..fafe4f0c --- /dev/null +++ b/src/Tests/Aardvark.Rendering.Tests/Tests/Other/IndexedGeometryTests.fs @@ -0,0 +1,42 @@ +namespace Aardvark.Rendering.Tests + +open Aardvark.Base +open Aardvark.Rendering +open Aardvark.SceneGraph +open Expecto + +module ``IndexedGeometry Tests`` = + + let clone = + test "Clone" { + let g = IndexedGeometryPrimitives.Box.solidBox Box3d.Unit C4b.Black |> IndexedGeometry.toIndexed + g.SingleAttributes <- SymDict.empty + let g_pos = g.IndexedAttributes.[DefaultSemantic.Positions] + let g_idx = g.IndexArray + + let gs = g.Clone() // shallow + let gs_pos = gs.IndexedAttributes.[DefaultSemantic.Positions] + let gs_idx = gs.IndexArray + + Expect.isFalse (obj.ReferenceEquals(g.SingleAttributes, gs.SingleAttributes)) "Single attributes not copied" + Expect.isFalse (obj.ReferenceEquals(g.IndexedAttributes, gs.IndexedAttributes)) "Indexed attributes not copied" + Expect.isTrue (obj.ReferenceEquals(g_pos, gs_pos)) "Attribute array copied" + Expect.isTrue (obj.ReferenceEquals(g_idx, gs_idx)) "Index array copied" + + let gd = g.Clone(shallowCopy = false) + let gd_pos = gd.IndexedAttributes.[DefaultSemantic.Positions] + let gd_idx = gd.IndexArray + + Expect.isFalse (obj.ReferenceEquals(g.SingleAttributes, gd.SingleAttributes)) "Single attributes not copied" + Expect.isFalse (obj.ReferenceEquals(g.IndexedAttributes, gd.IndexedAttributes)) "Indexed attributes not copied" + Expect.isFalse (obj.ReferenceEquals(g_pos, gd_pos)) "Attribute array not copied" + Expect.isFalse (obj.ReferenceEquals(g_idx, gd_idx)) "Index array not copied" + } + + [] + let tests = + testList "IndexedGeometry" [ + testList "Operations" [ + clone + ] + ] \ No newline at end of file From 629bbdb4b0b02f879336fdf9624e2abbdb7c4955 Mon Sep 17 00:00:00 2001 From: Martin Date: Wed, 15 May 2024 16:54:26 +0200 Subject: [PATCH 3/4] [IndexedGeometry] Fix Union() and add ToIndexed() overload --- .../Geometry/IndexedGeometry.fs | 194 ++++++++++++++---- .../Tests/Other/IndexedGeometryTests.fs | 113 +++++++--- 2 files changed, 238 insertions(+), 69 deletions(-) diff --git a/src/Aardvark.Rendering/Geometry/IndexedGeometry.fs b/src/Aardvark.Rendering/Geometry/IndexedGeometry.fs index f8a16ae7..119d3a08 100644 --- a/src/Aardvark.Rendering/Geometry/IndexedGeometry.fs +++ b/src/Aardvark.Rendering/Geometry/IndexedGeometry.fs @@ -3,19 +3,37 @@ open System open Aardvark.Base +[] +module private SymDictExtensions = + + type SymbolDict<'T> with + member inline x.CopyOrNull() = + if isNull x then null else x.Copy() + module private IndexHelpers = - let addOffset (offset : int) (index : Array) : Array = + let createIndex (indexType: Type) (count: int) = + let arr = Array.CreateInstance(indexType, count) + arr.Visit { new ArrayVisitor() with + member x.Run(data: 'T[]) = + let conv = PrimitiveValueConverter.converter + for i = 0 to count - 1 do + data.[i] <- conv i + arr + } + + let addOffset (startAt: int) (offset: int) (index: Array) : Array = if isNull index then null else - match index with - | :? array as x -> x |> Array.map ((+) offset) :> Array - | :? array as x -> x |> Array.map ((+) (int16 offset)) :> Array - | :? array as x -> x |> Array.map ((+) (uint32 offset)) :> Array - | :? array as x -> x |> Array.map ((+) (uint16 offset)) :> Array - | _ -> - raise <| ArgumentException($"Unsupported type {index.GetType()}.") + index.Visit { new ArrayVisitor() with + member x.Run(data: 'T[]) = + let ofUInt = PrimitiveValueConverter.converter + let toUInt = PrimitiveValueConverter.converter<'T, uint> + for i = startAt to data.Length - 1 do + data.[i] <- ofUInt (uint offset + toUInt data.[i]) + index + } let applyIndex (index : Array) (data : Array) = if isNull index || isNull data then @@ -152,26 +170,53 @@ module IndexedGeometryMode = type IndexedGeometry = class + /// Primitive topology of the geometry. val mutable public Mode : IndexedGeometryMode + + /// Index array (null for non-indexed geometry). val mutable public IndexArray : Array + + /// Per-vertex attributes (can be null). val mutable public IndexedAttributes : SymbolDict + + /// Single value attributes (can be null). val mutable public SingleAttributes : SymbolDict - member x.IsIndexed = not (isNull x.IndexArray) + /// Indicates whether the geometry is indexed. + member x.IsIndexed = + not (isNull x.IndexArray) + + /// Indicates whether the geometry has a non-zero face vertex count. + member inline x.IsEmpty = + x.FaceVertexCount = 0 + + /// Indicates whether the geometry is valid (i.e. it has a position attribute and all attribute arrays are of sufficient length) + member inline x.IsValid = + if isNull x.IndexedAttributes then false + else + match x.IndexedAttributes.TryGetValue DefaultSemantic.Positions with + | (true, positions) -> x.IndexedAttributes |> Seq.forall (fun attr -> attr.Value.Length >= positions.Length) + | _ -> false + /// Total number of vertices in the geometry. member x.VertexCount = if isNull x.IndexedAttributes then 0 else - match Seq.tryHead x.IndexedAttributes.Values with - | Some att -> att.Length - | _ -> 0 - + match x.IndexedAttributes.TryGetValue DefaultSemantic.Positions with + | (true, positions) -> positions.Length + | _ -> + match x.IndexedAttributes |> Seq.tryHead with + | Some (KeyValue(_, att)) -> att.Length + | _ -> 0 + + /// Effective number of vertices in the geometry (i.e. the index count if indexed and the vertex count if non-indexed). member x.FaceVertexCount = if isNull x.IndexArray then x.VertexCount else x.IndexArray.Length + /// Number of faces in the geometry. member x.FaceCount = IndexedGeometryMode.faceCount x.Mode x.FaceVertexCount @@ -179,11 +224,10 @@ type IndexedGeometry = ///If true, the index and attribute arrays are reused instead of being copied. member x.Clone(shallowCopy: bool) = let indices = - if isNull x.IndexArray then - null + if isNull x.IndexArray || shallowCopy then + x.IndexArray else - if shallowCopy then x.IndexArray - else x.IndexArray.Copy() + x.IndexArray.Copy() let indexedAttributes = if isNull x.IndexedAttributes then @@ -204,15 +248,23 @@ type IndexedGeometry = member x.Clone() = x.Clone(true) - member x.ToIndexed() = + /// Returns an indexed copy of the geometry. + /// If it is already indexed, it is returned unmodified. + member x.ToIndexed(indexType: Type) = if isNull x.IndexArray then - let res = x.Clone() - let count = x.FaceVertexCount - res.IndexArray <- Array.init count id - res + let copy = x.Clone() + copy.IndexArray <- IndexHelpers.createIndex indexType x.FaceVertexCount + copy else x + /// Returns an indexed copy of the geometry. + /// If it is already indexed, it is returned unmodified. + member x.ToIndexed() = + x.ToIndexed typeof + + /// Returns a non-indexed copy of the geometry. + /// If it is already non-indexed, it is returned unmodified. member x.ToNonIndexed() = if isNull x.IndexArray then x @@ -231,6 +283,8 @@ type IndexedGeometry = res + /// Returns a copy of the geometry with a non-stripped primitive topology. + /// If the topology is not line or triangle strips, the geometry is returned unmodified. member x.ToNonStripped() = match x.Mode with | IndexedGeometryMode.LineStrip -> @@ -248,43 +302,65 @@ type IndexedGeometry = | _ -> x + /// Returns a union of the geometry with another. + /// The geometries must have the same attributes and primitive topology. member x.Union(y : IndexedGeometry) = if x.Mode <> y.Mode then raise <| ArgumentException("IndexedGeometryMode must match.") let acceptedTopologies = [ - IndexedGeometryMode.PointList - IndexedGeometryMode.LineList - IndexedGeometryMode.TriangleList - IndexedGeometryMode.QuadList - ] + IndexedGeometryMode.PointList + IndexedGeometryMode.LineList + IndexedGeometryMode.TriangleList + IndexedGeometryMode.QuadList + ] if not <| List.contains x.Mode acceptedTopologies then raise <| ArgumentException($"IndexedGeometryMode must be one of {acceptedTopologies}.") if x.IsIndexed <> y.IsIndexed then - let a = if x.IsIndexed then x else y.ToIndexed() - let b = if y.IsIndexed then y else x.ToIndexed() + let a = if x.IsIndexed then x else x.ToIndexed <| y.IndexArray.GetType().GetElementType() + let b = if y.IsIndexed then y else y.ToIndexed <| x.IndexArray.GetType().GetElementType() a.Union b else let indices = - try - y.IndexArray - |> IndexHelpers.addOffset x.VertexCount - |> ArrayHelpers.concat x.IndexArray - with - | exn -> - raise <| ArgumentException($"Invalid indices: {exn.Message}") + if x.IsIndexed then + try + y.IndexArray + |> ArrayHelpers.concat x.IndexArray + |> IndexHelpers.addOffset x.IndexArray.Length x.VertexCount + with + | exn -> + raise <| ArgumentException($"Invalid indices: {exn.Message}") + else + null let singleAttributes = - if isNull x.SingleAttributes then y.SingleAttributes - elif isNull y.SingleAttributes then x.SingleAttributes - else SymDict.union [x.SingleAttributes; y.SingleAttributes] + if isNull x.SingleAttributes then y.SingleAttributes.CopyOrNull() + elif isNull y.SingleAttributes then x.SingleAttributes.CopyOrNull() + else + let r = SymDict.empty + + for (KeyValue(sem, a)) in x.SingleAttributes do + match y.SingleAttributes |> SymDict.tryFind sem with + | Some b when a <> b -> + raise <| ArgumentException($"Conflicting single value attribute {sem}.") + | _ -> + r.[sem] <- a + + for (KeyValue(sem, a)) in y.SingleAttributes do + match x.SingleAttributes |> SymDict.tryFind sem with + | Some b when a <> b -> + raise <| ArgumentException($"Conflicting single value attribute {sem}.") + | _ -> + r.[sem] <- a + + r let indexedAttributes = - if isNull x.IndexedAttributes then y.IndexedAttributes - elif isNull y.IndexedAttributes then x.IndexedAttributes + if isNull x.IndexedAttributes then y.IndexedAttributes.CopyOrNull() + elif isNull y.IndexedAttributes then x.IndexedAttributes.CopyOrNull() else let r = SymDict.empty @@ -329,18 +405,52 @@ type IndexedGeometry = [] module IndexedGeometry = - + + /// Returns the primitive topology of the given geometry. let inline mode (g : IndexedGeometry) = g.Mode + + /// Returns the index array of the given geometry (null if non-indexed). let inline indexArray (g : IndexedGeometry) = g.IndexArray + + /// Returns the per-vertex attributes of the given geometry (can be null). let inline indexedAttributes (g : IndexedGeometry) = g.IndexedAttributes + + /// Returns the single value attributes of the given geometry (can be null). let inline singleAttributes (g : IndexedGeometry) = g.SingleAttributes + /// Returns whether the given geometry has a non-zero face vertex count. + let inline isEmpty (g : IndexedGeometry) = g.IsEmpty + + /// Returns whether the given geometry is valid (i.e. it has a position attribute and all attribute arrays are of sufficient length) + let inline isValid (g : IndexedGeometry) = g.IsValid + + /// Returns whether the given geometry is indexed. let inline isIndexed (g : IndexedGeometry) = g.IsIndexed + + /// Returns the total number of vertices in the given geometry. let inline vertexCount (g : IndexedGeometry) = g.VertexCount + + /// Returns the effective number of vertices in the given geometry (i.e. the index count if indexed and the vertex count if non-indexed). let inline faceVertexCount (g : IndexedGeometry) = g.FaceVertexCount + + /// Returns the number of faces in the given geometry. let inline faceCount (g : IndexedGeometry) = g.FaceCount + + /// Returns a shallow copy of the given geometry (index and attribute arrays are reused). let inline clone (g : IndexedGeometry) = g.Clone() + + /// Returns an indexed copy of the given geometry. + /// If it is already indexed, it is returned unmodified. let inline toIndexed (g : IndexedGeometry) = g.ToIndexed() + + /// Returns a non-indexed copy of the geometry. + /// If it is already non-indexed, it is returned unmodified. let inline toNonIndexed (g : IndexedGeometry) = g.ToNonIndexed() + + /// Returns a copy of the geometry with a non-stripped primitive topology. + /// If the topology is not line or triangle strips, the geometry is returned unmodified. let inline toNonStripped (g : IndexedGeometry) = g.ToNonStripped() + + /// Returns a union of two geometries with another. + /// The geometries must have the same attributes and primitive topology. let inline union (a : IndexedGeometry) (b : IndexedGeometry) = a.Union b \ No newline at end of file diff --git a/src/Tests/Aardvark.Rendering.Tests/Tests/Other/IndexedGeometryTests.fs b/src/Tests/Aardvark.Rendering.Tests/Tests/Other/IndexedGeometryTests.fs index fafe4f0c..ff5f0e3c 100644 --- a/src/Tests/Aardvark.Rendering.Tests/Tests/Other/IndexedGeometryTests.fs +++ b/src/Tests/Aardvark.Rendering.Tests/Tests/Other/IndexedGeometryTests.fs @@ -7,36 +7,95 @@ open Expecto module ``IndexedGeometry Tests`` = - let clone = - test "Clone" { - let g = IndexedGeometryPrimitives.Box.solidBox Box3d.Unit C4b.Black |> IndexedGeometry.toIndexed - g.SingleAttributes <- SymDict.empty - let g_pos = g.IndexedAttributes.[DefaultSemantic.Positions] - let g_idx = g.IndexArray - - let gs = g.Clone() // shallow - let gs_pos = gs.IndexedAttributes.[DefaultSemantic.Positions] - let gs_idx = gs.IndexArray - - Expect.isFalse (obj.ReferenceEquals(g.SingleAttributes, gs.SingleAttributes)) "Single attributes not copied" - Expect.isFalse (obj.ReferenceEquals(g.IndexedAttributes, gs.IndexedAttributes)) "Indexed attributes not copied" - Expect.isTrue (obj.ReferenceEquals(g_pos, gs_pos)) "Attribute array copied" - Expect.isTrue (obj.ReferenceEquals(g_idx, gs_idx)) "Index array copied" - - let gd = g.Clone(shallowCopy = false) - let gd_pos = gd.IndexedAttributes.[DefaultSemantic.Positions] - let gd_idx = gd.IndexArray - - Expect.isFalse (obj.ReferenceEquals(g.SingleAttributes, gd.SingleAttributes)) "Single attributes not copied" - Expect.isFalse (obj.ReferenceEquals(g.IndexedAttributes, gd.IndexedAttributes)) "Indexed attributes not copied" - Expect.isFalse (obj.ReferenceEquals(g_pos, gd_pos)) "Attribute array not copied" - Expect.isFalse (obj.ReferenceEquals(g_idx, gd_idx)) "Index array not copied" - } + module Clone = + + let clone (shallow: bool) = + let name = if shallow then "shallow" else "deep" + + test $"Clone ({name})" { + let g = IndexedGeometryPrimitives.Box.solidBox Box3d.Unit C4b.Black |> IndexedGeometry.toIndexed + g.SingleAttributes <- SymDict.empty + let g_pos = g.IndexedAttributes.[DefaultSemantic.Positions] + let g_idx = g.IndexArray + + let gc = if shallow then g.Clone() else g.Clone(shallowCopy = false) + let gc_pos = gc.IndexedAttributes.[DefaultSemantic.Positions] + let gc_idx = gc.IndexArray + + Expect.isFalse (obj.ReferenceEquals(g.SingleAttributes, gc.SingleAttributes)) "Single attributes not copied" + Expect.isFalse (obj.ReferenceEquals(g.IndexedAttributes, gc.IndexedAttributes)) "Indexed attributes not copied" + + let expect = if shallow then Expect.isTrue else Expect.isFalse + expect (obj.ReferenceEquals(g_pos, gc_pos)) "Attribute array" + expect (obj.ReferenceEquals(g_idx, gc_idx)) "Index array" + + Expect.isTrue gc.IsValid "Invalid" + } + + module Union = + + let unionIndexed = + test "Union (indexed)" { + let a = IndexedGeometryPrimitives.Box.solidBox Box3d.Unit C4b.Black |> IndexedGeometry.toIndexed + a.IndexArray <- a.IndexArray |> unbox |> Array.map int16 + let b = IndexedGeometryPrimitives.Box.solidBox Box3d.Unit C4b.Black |> IndexedGeometry.toIndexed + b.IndexArray <- b.IndexArray |> unbox |> Array.map int16 + let c = IndexedGeometry.union a b + + let expected = + Array.concat [ + a.IndexArray |> unbox + b.IndexArray |> unbox |> Array.map ((+) (int16 a.VertexCount)) + ] + + Expect.equal (unbox c.IndexArray) expected "Unexpected indices" + Expect.equal c.FaceVertexCount (a.FaceVertexCount + b.FaceVertexCount) "Unexpected face vertex count" + Expect.isTrue c.IsValid "Invalid" + } + + let unionNonIndexed = + test "Union (non-indexed)" { + let a = IndexedGeometryPrimitives.Box.solidBox Box3d.Unit C4b.Black |> IndexedGeometry.toNonIndexed + let b = IndexedGeometryPrimitives.Box.solidBox Box3d.Unit C4b.Black |> IndexedGeometry.toNonIndexed + let c = IndexedGeometry.union a b + + Expect.isNull c.IndexArray "Unexpected index array" + Expect.equal c.FaceVertexCount (a.FaceVertexCount + b.FaceVertexCount) "Unexpected face vertex count" + Expect.isTrue c.IsValid "Invalid" + } + + let inline private unionNonIndexedAndIndexed (name: string) (mapIndex: int32 -> 'T) = + test $"Union (non-indexed & {name}-indexed)" { + let a = IndexedGeometryPrimitives.Box.solidBox Box3d.Unit C4b.Black |> IndexedGeometry.toNonIndexed + let b = IndexedGeometryPrimitives.Box.solidBox Box3d.Unit C4b.Black |> IndexedGeometry.toIndexed + b.IndexArray <- b.IndexArray |> unbox |> Array.map mapIndex + let c = IndexedGeometry.union a b + + let expected = + Array.concat [ + Array.init a.FaceVertexCount (id >> mapIndex) + b.IndexArray |> unbox<'T[]> |> Array.map ((+) (mapIndex a.VertexCount)) + ] + + Expect.equal (unbox<'T[]> c.IndexArray) expected "Unexpected indices" + Expect.isTrue c.IsValid "Invalid" + } + + let unionNonIndexedAndInt16 = unionNonIndexedAndIndexed "int16" int16 + let unionNonIndexedAndInt32 = unionNonIndexedAndIndexed "int32" int32 [] let tests = testList "IndexedGeometry" [ - testList "Operations" [ - clone + testList "Clone" [ + Clone.clone true + Clone.clone false + ] + + testList "Union" [ + Union.unionIndexed + Union.unionNonIndexed + Union.unionNonIndexedAndInt16 + Union.unionNonIndexedAndInt32 ] ] \ No newline at end of file From ef0679d3dab4b0f2777cd4a60ec2f0ad0c3836c6 Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 16 May 2024 10:23:33 +0200 Subject: [PATCH 4/4] [Sg] Use single value attributes for IndexedGeometry --- .../Resources/Buffers/SingleValueBuffer.fs | 23 ++- src/Aardvark.SceneGraph/SgFSharp.fs | 179 ++++++------------ 2 files changed, 78 insertions(+), 124 deletions(-) diff --git a/src/Aardvark.Rendering/Resources/Buffers/SingleValueBuffer.fs b/src/Aardvark.Rendering/Resources/Buffers/SingleValueBuffer.fs index eead7aa8..f04962a7 100644 --- a/src/Aardvark.Rendering/Resources/Buffers/SingleValueBuffer.fs +++ b/src/Aardvark.Rendering/Resources/Buffers/SingleValueBuffer.fs @@ -61,4 +61,25 @@ type SingleValueBuffer<'T when 'T : unmanaged>(value : aval<'T>) = member x.Accept(visitor) = value.Accept(visitor) member x.AllInputsProcessed(obj) = value.AllInputsProcessed(obj) member x.InputChanged(t, o) = value.InputChanged(t, o) - member x.Mark() = value.Mark() \ No newline at end of file + member x.Mark() = value.Mark() + +[] +module SingleValueBuffer = + + [] + module private GenericDispatch = + open System.Reflection + + [] + type Dispatcher() = + static member ToSingleValueBuffer<'T when 'T : unmanaged>(value: obj) : ISingleValueBuffer = + SingleValueBuffer<'T>(unbox<'T> value) + + module Method = + let private flags = BindingFlags.Static ||| BindingFlags.NonPublic ||| BindingFlags.Public + let toSingleValueBuffer = typeof.GetMethod("ToSingleValueBuffer", flags) + + /// Creates a single value buffer from an untyped value. + let create (value: obj) = + let mi = Method.toSingleValueBuffer.MakeGenericMethod [| value.GetType() |] + mi.Invoke(null, [| value |]) |> unbox \ No newline at end of file diff --git a/src/Aardvark.SceneGraph/SgFSharp.fs b/src/Aardvark.SceneGraph/SgFSharp.fs index 1228a810..f26ee1e8 100644 --- a/src/Aardvark.SceneGraph/SgFSharp.fs +++ b/src/Aardvark.SceneGraph/SgFSharp.fs @@ -39,30 +39,36 @@ module SgFSharp = module Caching = // Note: we need these caches because of the AVal.maps below - let bufferCache = ConditionalWeakTable() + let private cache = ConditionalWeakTable() - let bufferOfArray (m : aval<'a[]>) = - match bufferCache.TryGetValue m with - | (true, r) -> r + let private getOrCreate (create: 'T1 -> 'T2) (value: 'T1) : 'T2 = + match cache.TryGetValue value with + | (true, r) when r.GetType() = typeof<'T2> -> unbox<'T2> r | _ -> - let b = m |> AVal.map (fun a -> ArrayBuffer a :> IBuffer) - let r = BufferView(b, typeof<'a>) - bufferCache.Add(m, r) + let r = create value + cache.Add(value, r) r - let bufferOfTrafos (m : aval) = - match bufferCache.TryGetValue m with - | (true, r) -> r - | _ -> - let b = - m |> AVal.map (fun a -> - let a = a |> Array.map (Trafo.forward >> M44f) - ArrayBuffer a :> IBuffer - ) - - let r = BufferView(b, typeof) - bufferCache.Add(m, r) - r + let bufferOfArray (value: aval<'T[]>) : BufferView = + value |> getOrCreate (fun value -> + let b = value |> AVal.map (fun a -> ArrayBuffer a :> IBuffer) + BufferView(b, typeof<'T>) + ) + + let buffersOfTrafos (value: aval) : BufferView * BufferView = + value |> getOrCreate (fun value -> + let forward = value |> AVal.map (fun a -> + let a = a |> Array.map (Trafo.forward >> M44f) + ArrayBuffer a :> IBuffer + ) + + let backward = value |> AVal.map (fun a -> + let a = a |> Array.map (Trafo.backward >> M44f) + ArrayBuffer a :> IBuffer + ) + + BufferView(forward, typeof), BufferView(backward, typeof) + ) module Sg = open SgFSharpHelpers @@ -107,7 +113,7 @@ module SgFSharp = Sg.AdapterNode(o) :> ISg /// Combines the render objects in the given adaptive set. - let renderObjectSet (s : #aset) = + let renderObjectSet (s : #aset) = Sg.RenderObjectNode(s) :> ISg /// Applies the given activation function to the the given scene graph. @@ -790,80 +796,22 @@ module SgFSharp = let indirectDraw (mode : IndexedGeometryMode) (buffer : aval) = Sg.IndirectRenderNode(buffer, mode) :> ISg - /// Creates a draw call from the given indexed geometry. - let ofIndexedGeometry (g : IndexedGeometry) = - let attributes = - g.IndexedAttributes |> Seq.map (fun (KeyValue(k,v)) -> - let t = v.GetType().GetElementType() - let view = BufferView(~~(ArrayBuffer(v) :> IBuffer), t) - - k, view - ) |> Map.ofSeq - - - let index, faceVertexCount = - if g.IsIndexed then - g.IndexArray, g.IndexArray.Length - else - null, g.IndexedAttributes.[DefaultSemantic.Positions].Length - - let call = - DrawCallInfo( - FaceVertexCount = faceVertexCount, - FirstIndex = 0, - InstanceCount = 1, - FirstInstance = 0, - BaseVertex = 0 - ) - - let sg = Sg.VertexAttributeApplicator(attributes, Sg.RenderNode(call,g.Mode)) :> ISg - if not (isNull index) then - Sg.VertexIndexApplicator(BufferView.ofArray index, sg) :> ISg - else - sg - - /// Creates a draw call from the given indexed geometry and instance count. - let ofIndexedGeometryInstanced (g : IndexedGeometry) (instanceCount : int) = - let attributes = - g.IndexedAttributes |> Seq.map (fun (KeyValue(k,v)) -> - let t = v.GetType().GetElementType() - let view = BufferView(~~(ArrayBuffer(v) :> IBuffer), t) - - k, view - ) |> Map.ofSeq - - - let index, faceVertexCount = - if g.IsIndexed then - g.IndexArray, g.IndexArray.Length - else - null, g.IndexedAttributes.[DefaultSemantic.Positions].Length - - let call = - DrawCallInfo( - FaceVertexCount = faceVertexCount, - FirstIndex = 0, - InstanceCount = instanceCount, - FirstInstance = 0, - BaseVertex = 0 - ) - - let sg = Sg.VertexAttributeApplicator(attributes, Sg.RenderNode(call, g.Mode)) :> ISg - if not (isNull index) then - Sg.VertexIndexApplicator(BufferView.ofArray index, sg) :> ISg - else - sg - /// Creates a draw call from the given indexed geometry and an adpative instance count. let ofIndexedGeometryInstancedA (g : IndexedGeometry) (instanceCount : aval) = let attributes = - g.IndexedAttributes |> Seq.map (fun (KeyValue(k,v)) -> - let t = v.GetType().GetElementType() - let view = BufferView(~~(ArrayBuffer(v) :> IBuffer), t) + let indexed = + g.IndexedAttributes |> Seq.map (fun (KeyValue(sem, attr)) -> + sem, BufferView(attr) + ) |> Map.ofSeq - k, view - ) |> Map.ofSeq + let single = + if isNull g.SingleAttributes then Map.empty + else + g.SingleAttributes |> Seq.map (fun (KeyValue(sem, value)) -> + sem, BufferView(SingleValueBuffer.create value) + ) |> Map.ofSeq + Map.union indexed single let index, faceVertexCount = if g.IsIndexed then @@ -886,43 +834,28 @@ module SgFSharp = else sg - /// Creates a draw call, supplying the given transformations as per-instance attributes with - /// name DefaultSemantic.InstanceTrafo. - let instancedGeometry (trafos : aval) (g : IndexedGeometry) = - let vertexAttributes = - g.IndexedAttributes |> Seq.map (fun (KeyValue(k,v)) -> - let t = v.GetType().GetElementType() - let view = BufferView(~~(ArrayBuffer(v) :> IBuffer), t) - - k, view - ) |> Map.ofSeq - - let index, faceVertexCount = - if g.IsIndexed then - g.IndexArray, g.IndexArray.Length - else - null, g.IndexedAttributes.[DefaultSemantic.Positions].Length - - let call = trafos |> AVal.map (fun t -> - DrawCallInfo( - FaceVertexCount = faceVertexCount, - FirstIndex = 0, - InstanceCount = t.Length, - FirstInstance = 0, - BaseVertex = 0 - ) - ) + /// Creates a draw call from the given indexed geometry and instance count. + let ofIndexedGeometryInstanced (g : IndexedGeometry) (instanceCount : int) = + instanceCount |> AVal.constant |> ofIndexedGeometryInstancedA g - let sg = Sg.VertexAttributeApplicator(vertexAttributes, Sg.RenderNode(call, g.Mode)) :> ISg + /// Creates a draw call from the given indexed geometry. + let ofIndexedGeometry (g : IndexedGeometry) = + ofIndexedGeometryInstanced g 1 + /// Creates a draw call, supplying the given transformations as per-instance attributes + /// DefaultSemantic.InstanceTrafo and DefaultSemantic.InstanceTrafoInv. + let instancedGeometry (trafos : aval) (g : IndexedGeometry) = let sg = - if index <> null then - Sg.VertexIndexApplicator(BufferView.ofArray index, sg) :> ISg - else - sg + trafos + |> AVal.mapNonAdaptive Array.length + |> ofIndexedGeometryInstancedA g + + let forward, backward = Caching.buffersOfTrafos trafos - let view = Caching.bufferOfTrafos trafos - Sg.InstanceAttributeApplicator([DefaultSemantic.InstanceTrafo, view] |> Map.ofList, sg) :> ISg + Sg.InstanceAttributeApplicator([ + DefaultSemantic.InstanceTrafo, forward + DefaultSemantic.InstanceTrafoInv, backward + ] |> Map.ofList, sg) :> ISg // ================================================================================================================ // Bounding boxes