Skip to content

Added Low level type erasure to Vecs - #4

Open
Gesee-y wants to merge 11 commits into
RowDaBoat:mainfrom
Gesee-y:type-erasure
Open

Added Low level type erasure to Vecs#4
Gesee-y wants to merge 11 commits into
RowDaBoat:mainfrom
Gesee-y:type-erasure

Conversation

@Gesee-y

@Gesee-y Gesee-y commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Purpose

This PR's purpose is to implement unsafe type erasure for vecs, without using closures and virtual calls.
This allows for raw access between components pool of different type without cost.

What was done

  • Created vseq.nim: It implement seq but with unsafe operations allowing unsafe access without using the type. It was favored over Nim's seq because its layout varies with versions
  • Modified ecsseq.nim: To use the new VSeq type, and have type erased operations, without closures
  • Modified archetypes.nim: Removed the adders and movers, use raw byte manipulation functions. Also removed Tables in favor of raw seqs
  • Modified world.nim: Made the Immediate case no more use closure, it now has his own code path and for faster operations. Also, replaced adders with raw byte sequences (with their source zeroed, making it act as a move) that will be managed with the unsafe operations

What can be explored further

  • More extensive testing with ref types

Comment thread tests/tmove_debug.nim Outdated
Comment thread src/world.nim

template checkNotATuple[T](tup: typedesc[T]) =
when T is tuple:
when T is tuple and not defined(danger):

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when T is tuple is a compile time check, should not be skipped with -d:danger as it would allow compiling with an invalid type.

Comment thread src/world.nim
archetypeIds: seq[ArchetypeId] = @[]
archetypes: Table[ArchetypeId, Archetype]
builders: seq[Builder]
movers: seq[Mover]

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove lingering World.movers field.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(also remove world.movers.setLen(id + 1) set down below)

Comment thread src/componentid.nim
proc `==`*(a, b: ComponentId): bool {.borrow.}
proc `$`*(id: ComponentId): string = $id.int
template `[]`*[T](s: seq[T], i:ComponentId): T = s[i.int]
template `[]=`*[T](s: seq[T], i:ComponentId, val: T) = (s[i.int] = v)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Param is val, but is used as v, won't compile when used.

Comment thread src/ecsseq.nim

type Adder* =
proc(ecsSeq: var EcsSeqAny): int
proc(ecsSeq: var EcsSeqAny, itemPtr: pointer): int {.nimcall.}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adder type is dead, remove it.

Comment thread src/ecsseq.nim


type Mover* =
proc(fromEcsSeq: var EcsSeqAny, index: int, toEcsSeq: var EcsSeqAny): int {.nimcall.}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mover type is dead, remove it

Comment thread src/ecsseq.nim

proc ecsSeqAdder*[T](): Adder =
proc(ecsSeq: var EcsSeqAny, itemPtr: pointer): int {.nimcall.} =
cast[EcsSeq[T]](ecsSeq).add cast[ptr T](itemPtr)[]

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ecsSeqAdder is now dead code, remove it

Comment thread src/ecsseq.nim
fromEcsSeq.del index
result = typedToEcsSeq.add element
result = typedTo.add element
]#

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ecsSeqMover is commented code, remove it

Comment thread src/archetype.nim
componentIds*: seq[ComponentId]
componentLists*: Table[ComponentId, EcsSeqAny]
componentLists*: seq[EcsSeqAny]
componentSequences: seq[EcsSeqAny]

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about renaming these?
componentLists -> sparseComponents
componentSeqs -> denseComponents

Comment thread src/archetype.nim
id*: ArchetypeId
componentIds*: seq[ComponentId]
componentLists*: Table[ComponentId, EcsSeqAny]
componentLists*: seq[EcsSeqAny]

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

show.nim:29 calls archetype.componentLists.hasKey(componentId), leftover from when it was a Table

Comment thread src/archetype.nim
proc isEmpty*(archetype: Archetype): bool =
for componentList in archetype.componentLists.values:
for componentList in archetype.componentLists:
return componentList.len == 0

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use the dense one: componentSequences[0].len == 0

Comment thread src/vseq.nim
dealloc(s.data)
s.data = nil
s.len = 0
s.cap = 0

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

vseq.nim:31-37: =destroy does zeroMem + dealloc on the buffer without ever running element destructors, this is a leak (seq[T] does that). Also check setLen, del, clear, shrink, =copy and =dup, zeroing elements that may contain a ref, or copying without updating refcounts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants