1616import static java .lang .foreign .ValueLayout .JAVA_LONG ;
1717
1818public class Flecs implements AutoCloseable {
19-
19+
2020 private final MemorySegment nativeWorld ;
2121 private final Arena arena ;
2222 private final ComponentRegistry componentRegistry ;
2323 private boolean closed = false ;
2424 private final ThreadLocal <NameBuffer > threadLocalNameBuffer ;
25+ private final ThreadLocal <ComponentBuffer > threadLocalComponentBuffer ;
26+ private final ThreadLocal <EntityDescBuffer > threadLocalEntityDesc ;
2527 private final Map <Long , SystemCallbacks > systemCallbacks ;
2628 private final Map <Long , ObserverCallbacks > observerCallbacks ;
2729
2830 static {
2931 FlecsLoader .load ();
3032 }
3133
32- private static final class SystemCallbacks {
33- final Query .IterCallback iterCallback ;
34- final Query .RunCallback runCallback ;
35- final Query .EntityCallback entityCallback ;
36-
37- SystemCallbacks (Query .IterCallback iterCallback , Query .RunCallback runCallback , Query .EntityCallback entityCallback ) {
38- this .iterCallback = iterCallback ;
39- this .runCallback = runCallback ;
40- this .entityCallback = entityCallback ;
41- }
34+ private record SystemCallbacks (Query .IterCallback iterCallback , Query .RunCallback runCallback , Query .EntityCallback entityCallback ) {
4235 }
4336
44- private static final class ObserverCallbacks {
45- final Query .IterCallback iterCallback ;
46- final Query .RunCallback runCallback ;
47- final Query .EntityCallback entityCallback ;
48-
49- ObserverCallbacks (Query .IterCallback iterCallback , Query .RunCallback runCallback , Query .EntityCallback entityCallback ) {
50- this .iterCallback = iterCallback ;
51- this .runCallback = runCallback ;
52- this .entityCallback = entityCallback ;
53- }
37+ private record ObserverCallbacks (Query .IterCallback iterCallback , Query .RunCallback runCallback , Query .EntityCallback entityCallback ) {
5438 }
5539
56- private static final class NameBuffer {
57- MemorySegment segment ;
58- int capacity ;
40+ private static final class NameBuffer implements AutoCloseable {
41+ private Arena arena ;
42+ private MemorySegment segment ;
43+ private int capacity ;
5944
6045 NameBuffer (int initialCapacity ) {
6146 this .capacity = initialCapacity ;
62- this .segment = Arena .ofAuto ().allocate (initialCapacity );
47+ this .arena = Arena .ofConfined ();
48+ this .segment = this .arena .allocate (initialCapacity );
6349 }
6450
6551 MemorySegment ensure (int needed ) {
6652 if (needed > capacity ) {
67- Arena arena = Arena .ofAuto ();
68- segment = arena .allocate (needed );
69- capacity = needed ;
53+ this .arena .close ();
54+ this .arena = Arena .ofConfined ();
55+ this .segment = this .arena .allocate (needed );
56+ this .capacity = needed ;
7057 }
7158 return segment ;
7259 }
60+
61+ @ Override
62+ public void close () {
63+ if (this .arena != null ) {
64+ this .arena .close ();
65+ this .arena = null ;
66+ }
67+ }
68+ }
69+
70+ private static final class ComponentBuffer implements AutoCloseable {
71+ private Arena arena ;
72+ private MemorySegment segment ;
73+ private long capacity ;
74+
75+ ComponentBuffer (long initialCapacity ) {
76+ this .capacity = initialCapacity ;
77+ this .arena = Arena .ofConfined ();
78+ this .segment = this .arena .allocate (initialCapacity );
79+ }
80+
81+ MemorySegment ensure (long needed ) {
82+ if (needed > capacity ) {
83+ long newCapacity = Math .max (needed , capacity * 2 );
84+ this .arena .close ();
85+ this .arena = Arena .ofConfined ();
86+ this .segment = this .arena .allocate (newCapacity );
87+ this .capacity = newCapacity ;
88+ }
89+ return segment .asSlice (0 , needed );
90+ }
91+
92+ @ Override
93+ public void close () {
94+ if (this .arena != null ) {
95+ this .arena .close ();
96+ this .arena = null ;
97+ }
98+ }
99+ }
100+
101+ private static final class EntityDescBuffer implements AutoCloseable {
102+ private final Arena arena ;
103+ private final MemorySegment segment ;
104+
105+ EntityDescBuffer () {
106+ this .arena = Arena .ofConfined ();
107+ this .segment = ecs_entity_desc_t .allocate (this .arena );
108+ }
109+
110+ MemorySegment get () {
111+ return this .segment ;
112+ }
113+
114+ @ Override
115+ public void close () {
116+ this .arena .close ();
117+ }
73118 }
74119
75120 public Flecs () {
76121 this .arena = Arena .ofConfined ();
77122 this .nativeWorld = flecs_h .ecs_init ();
78-
123+
79124 if (this .nativeWorld == null || this .nativeWorld .address () == 0 ) {
80125 throw new IllegalStateException ("Flecs world initialization failed" );
81126 }
82127
83128 this .componentRegistry = new ComponentRegistry (this );
84129 this .threadLocalNameBuffer = ThreadLocal .withInitial (() -> new NameBuffer (64 ));
130+ this .threadLocalComponentBuffer = ThreadLocal .withInitial (() -> new ComponentBuffer (256 ));
131+ this .threadLocalEntityDesc = ThreadLocal .withInitial (EntityDescBuffer ::new );
85132 this .systemCallbacks = new ConcurrentHashMap <>();
86133 this .observerCallbacks = new ConcurrentHashMap <>();
87134 }
@@ -103,11 +150,10 @@ public long entity(String name) {
103150 nameSegment .asSlice (0 , len ).copyFrom (MemorySegment .ofArray (utf8 ));
104151 nameSegment .set (ValueLayout .JAVA_BYTE , len , (byte )0 );
105152
106- try (Arena tempArena = Arena .ofConfined ()) {
107- MemorySegment desc = ecs_entity_desc_t .allocate (tempArena );
108- ecs_entity_desc_t .name (desc , nameSegment );
109- return flecs_h .ecs_entity_init (this .nativeWorld , desc );
110- }
153+ MemorySegment desc = this .threadLocalEntityDesc .get ().get ();
154+ desc .fill ((byte ) 0 );
155+ ecs_entity_desc_t .name (desc , nameSegment );
156+ return flecs_h .ecs_entity_init (this .nativeWorld , desc );
111157 }
112158
113159 public Entity obtainEntity (long entityId ) {
@@ -117,18 +163,23 @@ public Entity obtainEntity(long entityId) {
117163 return new Entity (this , entityId );
118164 }
119165
166+ MemorySegment getComponentBuffer (long size ) {
167+ return this .threadLocalComponentBuffer .get ().ensure (size );
168+ }
169+
120170 public EcsLongList entityBulk (int count ) {
121171 this .checkClosed ();
172+ try (Arena tempArena = Arena .ofConfined ()) {
173+ MemorySegment desc = ecs_bulk_desc_t .allocate (tempArena );
174+ ecs_bulk_desc_t .count (desc , count );
175+ ecs_bulk_desc_t .entities (desc , MemorySegment .NULL );
122176
123- MemorySegment desc = ecs_bulk_desc_t .allocate (this .arena );
124- ecs_bulk_desc_t .count (desc , count );
125- ecs_bulk_desc_t .entities (desc , MemorySegment .NULL );
177+ MemorySegment idsSegment = flecs_h .ecs_bulk_init (this .nativeWorld , desc );
126178
127- MemorySegment idsSegment = flecs_h .ecs_bulk_init (nativeWorld , desc );
128-
129- EcsLongList ids = new EcsLongList (count );
130- ids .addAll (idsSegment .asSlice (0 , (long ) count * Long .BYTES ).toArray (JAVA_LONG ));
131- return ids ;
179+ EcsLongList ids = new EcsLongList (count );
180+ ids .addAll (idsSegment .asSlice (0 , (long ) count * Long .BYTES ).toArray (JAVA_LONG ));
181+ return ids ;
182+ }
132183 }
133184
134185 public long makeAlive (long entityId ) {
@@ -357,6 +408,12 @@ public void close() {
357408 }
358409 this .arena .close ();
359410 }
411+ this .threadLocalNameBuffer .get ().close ();
412+ this .threadLocalComponentBuffer .get ().close ();
413+ this .threadLocalEntityDesc .get ().close ();
414+ this .threadLocalNameBuffer .remove ();
415+ this .threadLocalComponentBuffer .remove ();
416+ this .threadLocalEntityDesc .remove ();
360417 }
361418
362419 @ Override
0 commit comments