Skip to content

Commit d87ad64

Browse files
committed
Made a pivotal change in how uniforms and vertex buffers are stored on the cpu side. They are no longer ordered by the gpu layout, which greatly improves usability and compatibility with other pipelines.
1 parent 7235be7 commit d87ad64

File tree

115 files changed

+4057
-2037
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

115 files changed

+4057
-2037
lines changed

VulkanDesign.txt

Lines changed: 3 additions & 241 deletions
Original file line numberDiff line numberDiff line change
@@ -1,243 +1,5 @@
11

2-
Stuck, again. I need a good way to access vertex data. The current system has some flaws:
3-
4-
1. Processes must assume the format and number of components of an attribute.
5-
This is especially concerning.
6-
2. Data cannot be accessed as struct buffers. This not a huge issue.
7-
3. Attributes are accessed by a string identifier.
8-
9-
I want to have a more concrete way to work with attributes. Something along these lines:
10-
11-
public interface PositionAttribute {
12-
void setPosition(int vertex, Vector3f position);
13-
Vector3f getPosition(int vertex);
14-
void setPositionAccess(DataAccess access);
15-
}
16-
17-
The problem is that a single attribute can get fairly bloated if you also add nice things like bulk setters and getters. Multiply that by every attribute you want, and it can get quite unwieldy. It also has that odd access setter on each attribute, which for whatever reason really annoys me.
18-
19-
What would actually be ideal is if everything could be represented by a simple "Vertex" struct.
20-
21-
struct Vertex {
22-
Vector3f position;
23-
Vector2f texCoord;
24-
Vector3f normal;
25-
}
26-
27-
(of course, in Java struct declaration is more complicated). The advantage is that all the vertex buffers can be very simply mapped as buffers of Vertex. It would also translate fairly well to instances.
28-
29-
struct Instance {
30-
Matrix4f worldViewProjection;
31-
Vector4f color;
32-
}
33-
34-
The problem is what to do with vertex data split across multiple buffers, which could very well be done if multiple shaders want to access the vertex positions, but not everything wants the texCoord and normal data. In which case it'd be represented as so:
35-
36-
struct VertexPart1 {
37-
Vector3f position;
38-
}
39-
struct VertexPart2 {
40-
Vector2f texCoord;
41-
Vector3f normal;
42-
}
43-
44-
User-side, this wouldn't be much of a problem. He should know what type of vertex he's working with. But the engine utilities don't necessarily know that. The vertex struct would have to implement an interface (possibly for each attribute, as discussed above), and the utility would have to cast each individual vertex to the desired type.
45-
46-
for (Vertex v : mesh.getVertices()) {
47-
((Position)v).setPosition(Vector3f.ZERO);
48-
}
49-
50-
It's messy and a pain to work with. It also doesn't properly address the dual vertex buffer situation. A possible solution to this is custom iterators which will automatically verify that an entire batch of vertices already meets the requirements. It would work something like this:
51-
52-
for (Position p : mesh.getVertices(Position.class)) {
53-
p.setPosition(Vector3f.ZERO);
54-
}
55-
for (TexCoord t : mesh.getVertices(TexCoord.class)) {
56-
t.setTexCoord(Vector2f.ZERO);
57-
}
58-
mesh.unmap(); // unmap all used buffers
59-
60-
getVertices() implementation may look something like this:
61-
62-
public class Mesh {
63-
private final Collection<VertexBuffer<?>> buffers = new ArrayList<>();
64-
private final Map<Class<? extends VertexAttribute>, VertexBuffer<?>> bufferLookup = new HashMap<>();
65-
public <T extends VertexAttribute> Iterable<T> getVertices(Class<T> type) {
66-
VertexBuffer<?> buf = bufferLookup.get(type);
67-
if (buf != null) {
68-
return buf.getVertices(type);
69-
}
70-
for (VertexBuffer<?> buffer : buffers) {
71-
if (type.isAssignableFrom(buffer.getVertexType())) {
72-
bufferLookup.put(type, buffer);
73-
return buffer.getVertices(type);
74-
}
75-
}
76-
throw new IllegalArgumentException("Mesh does not contain " + type);
77-
}
78-
}
79-
public class VertexBuffer <V extends Vertex> {
80-
private final Class<V> vertexType;
81-
private final GpuBuffer buffer;
82-
public <T extends VertexAttribute> Iterable<T> getVertices(Class<T> type) {
83-
if (!type.isAssignableFrom(vertexType)) {
84-
throw new ClassCastException("Vertex data is not " + type);
85-
}
86-
return (Iterable<T>)map();
87-
}
88-
public Iterable<V> map() {
89-
return buffer.map((Long address, Integer size) -> V.Buffer.create(address, size));
90-
}
91-
public Class<V> getVertexType() {
92-
return vertexType;
93-
}
94-
}
95-
96-
Unfortunately, this doesn't make accessing two different attributes at once very do-able. You'd have to do an awkward multi-iterator loop, which is just a pain to do. It would be better to have to do this:
97-
98-
List<Position> pos = mesh.getVertices(Position.class);
99-
List<TexCoord> tex = mesh.getVertices(TexCoord.class);
100-
pos.get(12).setPosition(tex.get(11).toVector3f());
101-
102-
How about this:
103-
104-
Position pos = mesh.getAttribute(Position.class);
105-
TexCoord tex = mesh.getAttribute(TexCoord.class);
106-
pos.set(12, 0f, 0f, 0f);
107-
Vector3f v = pos.get(11);
108-
for (Vector3f v : pos) {
109-
110-
}
111-
for (int i : Attributes.iterate(pos, tex)) {
112-
113-
}
114-
115-
public interface Attribute <T> extends Iterable<T> {
116-
117-
Attribute<T> map();
118-
119-
void set(int element, T value);
120-
121-
T get(int element);
122-
123-
int size();
124-
125-
// optional
126-
default T get(int element, T store) {
127-
return get(element);
128-
}
129-
130-
@Override
131-
default Iterator<T> iterator() {
132-
return new IteratorImpl<>(null);
133-
}
134-
135-
default IteratorImpl<T> iterator(T store) {
136-
return new IteratorImpl<>(store);
137-
}
138-
139-
class IteratorImpl <T> implements Iterator<T>, Iterable<T> {
140-
141-
private final Attribute<T> attr;
142-
private final T store;
143-
private int index = 0;
144-
145-
public IteratorImpl(Attribute<T> attr, T store) {
146-
this.attr = attr;
147-
this.store = store;
148-
}
149-
150-
public boolean hasNext() {
151-
return index < attr.size();
152-
}
153-
154-
public T next() {
155-
return attr.get(index++, store);
156-
}
157-
158-
public Iterator<T> iterator() {
159-
this.index = 0;
160-
return this;
161-
}
162-
163-
}
164-
165-
}
166-
public abstract class AbstractAttribute <T> implements Attribute<T> {
167-
168-
protected final VertexBuffer vertexBuffer;
169-
protected final int stride, offset, size;
170-
protected ByteBuffer buffer;
171-
172-
public AbstractAttribute(VertexBuffer vertexBuffer, int stride, int offset, int size) {
173-
this.vertexBuffer = vertexBuffer;
174-
}
175-
176-
}
177-
public class Position implements Attribute<Vector3f> {
178-
179-
private VertexBuffer buffer;
180-
private int stride, offset;
181-
182-
@Override
183-
public void set(int element, Vector3f value) {
184-
set(element, value.x, value.y, value.z);
185-
}
186-
187-
@Override
188-
public Vector3f get(int element) {
189-
Vector3f store = new Vector3f();
190-
ByteBuffer buf = buffer.map().position(element * stride + offset);
191-
store.x = buf.getFloat();
192-
store.y = buf.getFloat();
193-
store.z = buf.getFloat();
194-
return store;
195-
}
196-
197-
public void set(int element, float x, float y, float z) {
198-
buffer.map()
199-
.position(element * stride + offset)
200-
.putFloat(x).putFloat(y).putFloat(z);
201-
}
202-
203-
}
204-
205-
If mappings are frequent -> streaming buffer
206-
If mappings are less frequent -> dynamic buffer
207-
If only one mapping occurs OR a mapping hasn't occured in a very long time -> static buffer
208-
209-
Static AND dynamic
210-
Build
211-
1. Create GPU buffer (device local)
212-
2. Create CPU buffer (nio)
213-
CPU -> GPU
214-
1. Acquire staging buffer (host visible and coherent)
215-
2. Map staging buffer
216-
3. Copy CPU buffer -> staging buffer
217-
4. Record copy staging buffer -> GPU buffer
218-
5. Unmap staging buffer
219-
--- wait for copy to complete
220-
6. Release staging buffer
221-
GPU -> CPU
222-
1. Acquire staging buffer (host visible and coherent)
223-
2. Copy GPU buffer -> staging buffer
224-
--- wait for copy to complete
225-
3. Map staging buffer
226-
4. Copy staging buffer -> CPU buffer
227-
OR read directly from staging buffer
228-
(depends on the nature of the read op)
229-
5. Unmap staging buffer
230-
6. Release staging buffer
231-
Streaming
232-
Build
233-
1. Create GPU buffers per frame (host visible and coherent)
234-
2. Create CPU buffer (nio)
235-
CPU -> GPU
236-
1. Map frame GPU buffer (if not already mapped)
237-
2. Copy CPU buffer -> frame GPU buffer
238-
3. Unmap frame GPU buffer (if necessary)
239-
GPU -> CPU
240-
1. Map a GPU buffer (if not already mapped)
241-
2. Copy GPU buffer -> CPU buffer
242-
3. Unmap GPU buffer (if necessary)
2+
Material should be split into two different classes:
2433

4+
1. Associate values with uniform names. This is what NewMaterial is doing.
5+
2. Associate uniform names with descriptor bindings. Previously, this role would've been filled by MaterialDef.

jme3-core/build.gradle

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,6 @@ dependencies {
2020
testImplementation project(':jme3-desktop')
2121
testRuntimeOnly project(':jme3-plugins')
2222

23-
// jackson yaml parsing
24-
implementation "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.14.2"
25-
2623
// Use LWJGL3 directly in core. This destroys LWJGL2 and JOGL compatibility.
2724
api (libs.lwjgl3.awt) {
2825
exclude group: 'org.lwjgl', module: 'lwjgl'
@@ -73,6 +70,7 @@ dependencies {
7370
runtimeOnly(variantOf(libs.lwjgl3.openal){ classifier('natives-linux-arm64') })
7471
runtimeOnly(variantOf(libs.lwjgl3.openal){ classifier('natives-macos') })
7572
runtimeOnly(variantOf(libs.lwjgl3.openal){ classifier('natives-macos-arm64') })
73+
//runtimeOnly(variantOf(libs.lwjgl3.vulkan) { classifier('natives-linux') })
7674
runtimeOnly(variantOf(libs.lwjgl3.shaderc) { classifier('natives-linux') })
7775

7876
}

0 commit comments

Comments
 (0)