anchor.diff:              member x.GetReader() = x.GetReader()
               member x.ElementType = elementType
   
  +        new(t) = ChangeableBuffer(t, 0)
  +
  +    type GeometryPacker(attributeTypes : Map<Symbol, Type>) =
  +        inherit Mod.AbstractMod<RangeSet>()
  +
  +        let manager = MemoryManager.createNop()
  +        let locations = ConcurrentDictionary<IndexedGeometry, managedptr>()
  +        let mutable buffers = ConcurrentDictionary<Symbol, Option<ChangeableBuffer>>()
  +        let mutable ranges = RangeSet.empty
  +        let mutable marked = 1
  +
  +        let writeAttribute (sem : Symbol) (region : managedptr) (buffer : ChangeableBuffer) (source : IndexedGeometry) =
  +            let cap = nativeint manager.Capacity
  +            if buffer.Capacity <> cap then buffer.Resize(cap)
  +            match source.IndexedAttributes.TryGetValue(sem) with
  +                | (true, arr) ->
  +                    buffer.Write(int region.Offset, arr, region.Size)
  +                | _ ->
  +                    // TODO: write NullBuffer content or 0 here
  +                    ()
  +
  +        let tryGetBuffer (sem : Symbol) =
  +            let mutable isNew = false
  +            let result = 
  +                buffers.GetOrAdd(sem, fun sem ->
  +                    match Map.tryFind sem attributeTypes with
  +                        | Some t ->
  +                            isNew <- true
  +                            let b = ChangeableBuffer(t, manager.Capacity)
  +                            Some b
  +                        | None ->
  +                            None
  +                )
  +
  +            if isNew then
  +                let b = result.Value // safe here
  +                for (KeyValue(g,region)) in locations do
  +                    writeAttribute sem region b g
  +
  +            result
  +                     
  +        member private x.AddRange (ptr : managedptr) =
  +            let r = Range1i(int ptr.Offset, ptr.Size - 1)
  +            Interlocked.Change(&ranges, RangeSet.insert r) |> ignore
  +            if Interlocked.Exchange(&marked, 1) = 0 then
  +                transact (fun () -> x.MarkOutdated())
  +
  +        member private x.RemoveRange (ptr : managedptr) =
  +            let r = Range1i(int ptr.Offset, ptr.Size - 1)
  +            Interlocked.Change(&ranges, RangeSet.remove r) |> ignore
  +            if Interlocked.Exchange(&marked, 1) = 0 then
  +                transact (fun () -> x.MarkOutdated())
  +           
  +        member x.Add (g : IndexedGeometry) =
  +            let mutable isNew = false
  +
  +            let region = 
  +                locations.GetOrAdd(g, fun g ->
  +                    let faceVertexCount =
  +                        if isNull g.IndexArray then
  +                            let att = g.IndexedAttributes.Values |> Seq.head   
  +                            att.Length
  +                        else
  +                            g.IndexArray.Length
  +                    
  +                    isNew <- true
  +                    manager.Alloc(faceVertexCount)
  +                )
  +            
  +            if isNew then
  +                
  +                let cap = nativeint manager.Capacity
  +                for (KeyValue(sem, buffer)) in buffers do
  +                    match buffer with
  +                        | Some buffer ->
  +                            writeAttribute sem region buffer g
  +                        | _ ->
  +                            ()
  +                true
  +            else
  +                false
  +
  +        member x.Remove (g : IndexedGeometry) =
  +            match locations.TryRemove g with
  +                | (true, region) ->
  +                    manager.Free(region)
  +                    true
  +                | _ ->
  +                    false
  +
  +        member x.TryGetBuffer (sem : Symbol) =
  +            match tryGetBuffer sem with
  +                | Some b ->
  +                    b :> IMod<IBuffer> |> Some
  +                | None ->
  +                    None
  +
  +        member x.Dispose() =
  +            let old = Interlocked.Exchange(&buffers, ConcurrentDictionary())
  +            if old.Count > 0 then
  +                old.Values |> Seq.iter (fun b ->
  +                    match b with
  +                        | Some b -> b.Dispose()
  +                        | _ -> ()
  +                )
  +                old.Clear()
  +
  +        override x.Compute() =
  +            marked <- 0
  +            ranges
  +
  +        
       
  -    
  -
  -
  -
  +
  +
  +
  
positive.code:         mutable VertexAttributes    : IAttributeProvider
                  
          mutable Uniforms : IUniformProvider
      }  
      interface IRenderObject with
          member x.RenderPass = x.RenderPass
  
negative.code:           InstanceAttributes = emptyAttributes
            VertexAttributes = emptyAttributes
            Uniforms = emptyUniforms
            Activate = nopActivate
          }
  
  
  