Skip to content

Commit 7810eb4

Browse files
committed
feature ; view component
1 parent 062c1ac commit 7810eb4

7 files changed

Lines changed: 184 additions & 59 deletions

File tree

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.github.elebras1.flecs.examples;
2+
3+
public class ComponentViewExample {
4+
5+
private static final int ENTITY_COUNT = 1_000_000;
6+
7+
public static void main(String[] args) {
8+
}
9+
}
10+

src/main/java/com/github/elebras1/flecs/Entity.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,42 @@ public <T> T get(Class<T> componentClass, long target) {
199199
return component.read(dataSegment);
200200
}
201201

202+
@SuppressWarnings("unchecked")
203+
public <T> T getView(Class<?> componentClass) {
204+
ComponentView view = FlecsContext.CURRENT_CACHE.get().get(componentClass);
205+
206+
long componentId = this.world.componentRegistry().getComponentId(componentClass);
207+
208+
MemorySegment dataPtr = flecs_h.ecs_get_id(this.world.nativeHandle(), this.id, componentId);
209+
210+
if (dataPtr == null || dataPtr.address() == 0) {
211+
return null;
212+
}
213+
214+
view.setMemorySegment(dataPtr);
215+
216+
return (T) view;
217+
}
218+
219+
@SuppressWarnings("unchecked")
220+
public <T> T getView(Class<?> componentClass, long target) {
221+
ComponentView view = FlecsContext.CURRENT_CACHE.get().get(componentClass);
222+
223+
long componentId = this.world.componentRegistry().getComponentId(componentClass);
224+
225+
long pairId = flecs_h.ecs_make_pair(componentId, target);
226+
227+
MemorySegment dataPtr = flecs_h.ecs_get_id(this.world.nativeHandle(), this.id, pairId);
228+
229+
if (dataPtr == null || dataPtr.address() == 0) {
230+
return null;
231+
}
232+
233+
view.setMemorySegment(dataPtr);
234+
235+
return (T) view;
236+
}
237+
202238
public void enable() {
203239
flecs_h.ecs_enable(this.world.nativeHandle(), this.id, true);
204240
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.github.elebras1.flecs;
2+
3+
import java.util.IdentityHashMap;
4+
import java.util.Map;
5+
6+
public class FlecsContext {
7+
public static final ScopedValue<ViewCache> CURRENT_CACHE = ScopedValue.newInstance();
8+
9+
public static class ViewCache {
10+
private final Map<Class<?>, ComponentView> views;
11+
12+
public ViewCache() {
13+
this.views = new IdentityHashMap<>(8);
14+
}
15+
16+
public ComponentView get(Class<?> componentClass) {
17+
ComponentView view = this.views.get(componentClass);
18+
if (view != null) {
19+
return view;
20+
}
21+
22+
view = ComponentMap.getView(componentClass);
23+
24+
if (view != null) {
25+
this.views.put(componentClass, view);
26+
}
27+
28+
return view;
29+
}
30+
}
31+
}

src/main/java/com/github/elebras1/flecs/ObserverBuilder.java

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,11 @@ public FlecsObserver iter(Query.IterCallback callback) {
161161
this.iterCallback = callback;
162162

163163
MemorySegment callbackStub = ecs_iter_action_t.allocate(it -> {
164-
Iter iter = new Iter(it, this.world);
165-
callback.accept(iter);
164+
var cache = new FlecsContext.ViewCache();
165+
ScopedValue.where(FlecsContext.CURRENT_CACHE, cache).run(() -> {
166+
Iter iter = new Iter(it, this.world);
167+
callback.accept(iter);
168+
});
166169
}, this.world.arena());
167170

168171
ecs_observer_desc_t.callback(this.desc, callbackStub);
@@ -175,7 +178,8 @@ public FlecsObserver run(Query.RunCallback callback) {
175178

176179
MemorySegment callbackStub = ecs_run_action_t.allocate(it -> {
177180
Iter iter = new Iter(it, this.world);
178-
callback.accept(iter);
181+
var cache = new FlecsContext.ViewCache();
182+
ScopedValue.where(FlecsContext.CURRENT_CACHE, cache).run(() -> callback.accept(iter));
179183
}, this.world.arena());
180184

181185
ecs_observer_desc_t.run(this.desc, callbackStub);
@@ -189,11 +193,13 @@ public FlecsObserver each(Query.EntityCallback callback) {
189193
MemorySegment callbackStub = ecs_iter_action_t.allocate(it -> {
190194
int count = ecs_iter_t.count(it);
191195
MemorySegment entities = ecs_iter_t.entities(it);
192-
193-
for (int i = 0; i < count; i++) {
194-
long entityId = entities.getAtIndex(ValueLayout.JAVA_LONG, i);
195-
callback.accept(entityId);
196-
}
196+
var cache = new FlecsContext.ViewCache();
197+
ScopedValue.where(FlecsContext.CURRENT_CACHE, cache).run(() -> {
198+
for (int i = 0; i < count; i++) {
199+
long entityId = entities.getAtIndex(ValueLayout.JAVA_LONG, i);
200+
callback.accept(entityId);
201+
}
202+
});
197203
}, this.world.arena());
198204

199205
ecs_observer_desc_t.callback(this.desc, callbackStub);

src/main/java/com/github/elebras1/flecs/Query.java

Lines changed: 35 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -35,52 +35,56 @@ public interface RunCallback {
3535

3636
public void each(EntityCallback callback) {
3737
this.checkClosed();
38+
var cache = new FlecsContext.ViewCache();
39+
ScopedValue.where(FlecsContext.CURRENT_CACHE, cache).run(() -> {
40+
MemorySegment iter = flecs_h.ecs_query_iter(this.arena, this.world.nativeHandle(), this.nativeQuery);
41+
if (iter == null || iter.address() == 0) {
42+
throw new IllegalStateException("ecs_query_iter returned a null iterator");
43+
}
3844

39-
MemorySegment iter = flecs_h.ecs_query_iter(this.arena, this.world.nativeHandle(), this.nativeQuery);
40-
41-
if (iter == null || iter.address() == 0) {
42-
throw new IllegalStateException("ecs_query_iter returned a null iterator");
43-
}
44-
45-
while (flecs_h.ecs_iter_next(iter)) {
46-
int count = ecs_iter_t.count(iter);
47-
MemorySegment entities = ecs_iter_t.entities(iter);
45+
while (flecs_h.ecs_iter_next(iter)) {
46+
int count = ecs_iter_t.count(iter);
47+
MemorySegment entities = ecs_iter_t.entities(iter);
4848

49-
for (int i = 0; i < count; i++) {
50-
long entityId = entities.getAtIndex(ValueLayout.JAVA_LONG, i);
51-
callback.accept(entityId);
49+
for (int i = 0; i < count; i++) {
50+
long entityId = entities.getAtIndex(ValueLayout.JAVA_LONG, i);
51+
callback.accept(entityId);
52+
}
5253
}
53-
}
54+
});
5455
}
5556

5657
public void iter(IterCallback callback) {
5758
this.checkClosed();
59+
var cache = new FlecsContext.ViewCache();
60+
ScopedValue.where(FlecsContext.CURRENT_CACHE, cache).run(() -> {
61+
MemorySegment iter = flecs_h.ecs_query_iter(this.arena, this.world.nativeHandle(), this.nativeQuery);
62+
if (iter == null || iter.address() == 0) {
63+
throw new IllegalStateException("ecs_query_iter returned a null iterator");
64+
}
5865

59-
MemorySegment iter = flecs_h.ecs_query_iter(this.arena, this.world.nativeHandle(), this.nativeQuery);
60-
61-
if (iter == null || iter.address() == 0) {
62-
throw new IllegalStateException("ecs_query_iter returned a null iterator");
63-
}
64-
65-
Iter it = new Iter(iter, this.world);
66+
Iter it = new Iter(iter, this.world);
6667

67-
while (flecs_h.ecs_iter_next(iter)) {
68-
it.setNativeIter(iter);
69-
callback.accept(it);
70-
}
68+
while (flecs_h.ecs_iter_next(iter)) {
69+
it.setNativeIter(iter);
70+
callback.accept(it);
71+
}
72+
});
7173
}
7274

7375
public void run(RunCallback callback) {
7476
this.checkClosed();
77+
var cache = new FlecsContext.ViewCache();
78+
ScopedValue.where(FlecsContext.CURRENT_CACHE, cache).run(() -> {
79+
MemorySegment iter = flecs_h.ecs_query_iter(this.arena, this.world.nativeHandle(), this.nativeQuery);
7580

76-
MemorySegment iter = flecs_h.ecs_query_iter(this.arena, this.world.nativeHandle(), this.nativeQuery);
77-
78-
if (iter == null || iter.address() == 0) {
79-
throw new IllegalStateException("ecs_query_iter returned a null iterator");
80-
}
81+
if (iter == null || iter.address() == 0) {
82+
throw new IllegalStateException("ecs_query_iter returned a null iterator");
83+
}
8184

82-
Iter it = new Iter(iter, this.world);
83-
callback.accept(it);
85+
Iter it = new Iter(iter, this.world);
86+
callback.accept(it);
87+
});
8488
}
8589

8690
public int count() {

src/main/java/com/github/elebras1/flecs/SystemBuilder.java

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -183,14 +183,17 @@ public <T> SystemBuilder read(Class<T> componentClass) {
183183

184184
public FlecsSystem iter(Query.IterCallback callback) {
185185
this.iterCallback = callback;
186-
186+
187187
MemorySegment callbackStub = ecs_iter_action_t.allocate(it -> {
188188
Iter iter = new Iter(it, this.world);
189-
callback.accept(iter);
189+
var cache = new FlecsContext.ViewCache();
190+
ScopedValue.where(FlecsContext.CURRENT_CACHE, cache).run(() -> {
191+
callback.accept(iter);
192+
});
190193
}, this.world.arena());
191-
194+
192195
ecs_system_desc_t.callback(this.desc, callbackStub);
193-
196+
194197
return build();
195198
}
196199

@@ -199,7 +202,10 @@ public FlecsSystem run(Query.RunCallback callback) {
199202

200203
MemorySegment callbackStub = ecs_run_action_t.allocate(it -> {
201204
Iter iter = new Iter(it, this.world);
202-
callback.accept(iter);
205+
var cache = new FlecsContext.ViewCache();
206+
ScopedValue.where(FlecsContext.CURRENT_CACHE, cache).run(() -> {
207+
callback.accept(iter);
208+
});
203209
}, this.world.arena());
204210

205211
ecs_system_desc_t.run(this.desc, callbackStub);
@@ -209,19 +215,22 @@ public FlecsSystem run(Query.RunCallback callback) {
209215

210216
public FlecsSystem each(Query.EntityCallback callback) {
211217
this.entityCallback = callback;
212-
218+
213219
MemorySegment callbackStub = ecs_iter_action_t.allocate(it -> {
214-
int count = ecs_iter_t.count(it);
215-
MemorySegment entities = ecs_iter_t.entities(it);
216-
217-
for (int i = 0; i < count; i++) {
218-
long entityId = entities.getAtIndex(ValueLayout.JAVA_LONG, i);
219-
callback.accept(entityId);
220-
}
220+
var cache = new FlecsContext.ViewCache();
221+
ScopedValue.where(FlecsContext.CURRENT_CACHE, cache).run(() -> {
222+
int count = ecs_iter_t.count(it);
223+
MemorySegment entities = ecs_iter_t.entities(it);
224+
225+
for (int i = 0; i < count; i++) {
226+
long entityId = entities.getAtIndex(ValueLayout.JAVA_LONG, i);
227+
callback.accept(entityId);
228+
}
229+
});
221230
}, this.world.arena());
222-
231+
223232
ecs_system_desc_t.callback(this.desc, callbackStub);
224-
233+
225234
return build();
226235
}
227236

src/main/java/com/github/elebras1/flecs/processor/ComponentMapGenerator.java

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,24 @@
99
import java.util.IdentityHashMap;
1010
import java.util.List;
1111
import java.util.Map;
12+
import java.util.function.Supplier;
1213

1314
public class ComponentMapGenerator {
1415

1516
private static final ClassName COMPONENT_INTERFACE = ClassName.get("com.github.elebras1.flecs", "Component");
17+
private static final ClassName COMPONENT_VIEW_INTERFACE = ClassName.get("com.github.elebras1.flecs", "ComponentView");
1618
private static final String MAP_PACKAGE = "com.github.elebras1.flecs";
1719
private static final String MAP_COMPONENT_CLASS_NAME = "ComponentMap";
1820

1921
public JavaFile generateComponentMap(List<TypeElement> components) {
2022
TypeSpec.Builder mapClass = TypeSpec.classBuilder(MAP_COMPONENT_CLASS_NAME)
2123
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
2224
.addField(this.createMapField(components.size()))
25+
.addField(this.createViewMapField(components.size()))
2326
.addStaticBlock(this.createStaticInitializer(components))
2427
.addMethod(this.createConstructor())
25-
.addMethod(this.createGetInstanceMethod());
28+
.addMethod(this.createGetInstanceMethod())
29+
.addMethod(this.createGetViewMethod());
2630

2731
return JavaFile.builder(MAP_PACKAGE, mapClass.build())
2832
.addFileComment("Generated by ComponentMapGenerator.")
@@ -42,18 +46,32 @@ private FieldSpec createMapField(int componentsSize) {
4246
.build();
4347
}
4448

49+
private FieldSpec createViewMapField(int componentsSize) {
50+
ParameterizedTypeName supplierType = ParameterizedTypeName.get(ClassName.get(Supplier.class), COMPONENT_VIEW_INTERFACE);
51+
ParameterizedTypeName mapType = ParameterizedTypeName.get(
52+
ClassName.get(Map.class),
53+
ParameterizedTypeName.get(ClassName.get(Class.class), WildcardTypeName.subtypeOf(Object.class)),
54+
supplierType
55+
);
56+
57+
return FieldSpec.builder(mapType, "VIEW_MAP", Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
58+
.initializer("new $T<>($L)", IdentityHashMap.class, componentsSize)
59+
.build();
60+
}
61+
4562
private CodeBlock createStaticInitializer(List<TypeElement> components) {
4663
CodeBlock.Builder builder = CodeBlock.builder();
4764

4865
for (TypeElement component : components) {
4966
String packageName = this.getPackageName(component);
5067
String recordName = component.getSimpleName().toString();
51-
String componentClassName = recordName + "Component";
5268

5369
ClassName recordClass = ClassName.get(packageName, recordName);
54-
ClassName componentClass = ClassName.get(packageName, componentClassName);
70+
ClassName componentClass = ClassName.get(packageName, recordName + "Component");
71+
ClassName viewClass = ClassName.get(packageName, recordName + "ComponentView");
5572

5673
builder.addStatement("COMPONENT_MAP.put($T.class, $T.getInstance())", recordClass, componentClass);
74+
builder.addStatement("VIEW_MAP.put($T.class, $T::new)", recordClass, viewClass);
5775
}
5876

5977
return builder.build();
@@ -81,12 +99,23 @@ private MethodSpec createGetInstanceMethod() {
8199
return method.build();
82100
}
83101

102+
private MethodSpec createGetViewMethod() {
103+
MethodSpec.Builder method = MethodSpec.methodBuilder("getView")
104+
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
105+
.addTypeVariable(TypeVariableName.get("T"))
106+
.addParameter(ParameterizedTypeName.get(ClassName.get(Class.class), TypeVariableName.get("T")), "componentClass")
107+
.returns(COMPONENT_VIEW_INTERFACE)
108+
.addStatement("$T supplier = VIEW_MAP.get(componentClass)", ParameterizedTypeName.get(ClassName.get(Supplier.class), COMPONENT_VIEW_INTERFACE))
109+
.addStatement("return supplier != null ? supplier.get() : null");
110+
111+
return method.build();
112+
}
113+
84114
private String getPackageName(TypeElement element) {
85115
Element current = element.getEnclosingElement();
86116
while (current != null && !(current instanceof PackageElement)) {
87117
current = current.getEnclosingElement();
88118
}
89119
return current != null ? ((PackageElement) current).getQualifiedName().toString() : "";
90120
}
91-
}
92-
121+
}

0 commit comments

Comments
 (0)