Progress on UI Renderer, getting to a good level :)

This commit is contained in:
2026-08-20 23:28:47 +02:00
parent 442d1ee654
commit 4e37253988
15 changed files with 425 additions and 37 deletions
@@ -1,6 +1,7 @@
package speiger.src.coreengine.platform.graphics.api.core; package speiger.src.coreengine.platform.graphics.api.core;
import speiger.src.coreengine.platform.graphics.api.buffer.VertexBuffer; import speiger.src.coreengine.platform.graphics.api.buffer.VertexBuffer;
import speiger.src.coreengine.platform.graphics.api.mesh.DynamicMesh;
import speiger.src.coreengine.platform.graphics.api.mesh.Mesh; import speiger.src.coreengine.platform.graphics.api.mesh.Mesh;
import speiger.src.coreengine.platform.graphics.api.shader.ShaderPipeline; import speiger.src.coreengine.platform.graphics.api.shader.ShaderPipeline;
import speiger.src.coreengine.platform.graphics.api.texture.Texture; import speiger.src.coreengine.platform.graphics.api.texture.Texture;
@@ -13,7 +14,10 @@ public interface GraphicsCommandBuffer extends GraphicsResource, CommandBuffer {
GraphicsCommandBuffer setScissors(int x, int y, int width, int height); GraphicsCommandBuffer setScissors(int x, int y, int width, int height);
GraphicsCommandBuffer pipeline(ShaderPipeline pipeline); GraphicsCommandBuffer pipeline(ShaderPipeline pipeline);
GraphicsCommandBuffer mesh(Mesh mesh); GraphicsCommandBuffer mesh(Mesh mesh);
GraphicsCommandBuffer mesh(DynamicMesh mesh);
GraphicsCommandBuffer texture(int binding, SampledTexture texture); GraphicsCommandBuffer texture(int binding, SampledTexture texture);
GraphicsCommandBuffer texture(int binding, Texture texture, AccessType access); GraphicsCommandBuffer texture(int binding, Texture texture, AccessType access);
@@ -6,6 +6,7 @@ import speiger.src.coreengine.platform.graphics.api.buffer.VertexBuffer;
import speiger.src.coreengine.platform.graphics.api.buffer.states.BufferState; import speiger.src.coreengine.platform.graphics.api.buffer.states.BufferState;
import speiger.src.coreengine.platform.graphics.api.buffer.states.BufferType; import speiger.src.coreengine.platform.graphics.api.buffer.states.BufferType;
import speiger.src.coreengine.platform.graphics.api.compute.ComputePipeline; import speiger.src.coreengine.platform.graphics.api.compute.ComputePipeline;
import speiger.src.coreengine.platform.graphics.api.mesh.DynamicMesh;
import speiger.src.coreengine.platform.graphics.api.mesh.Mesh; import speiger.src.coreengine.platform.graphics.api.mesh.Mesh;
import speiger.src.coreengine.platform.graphics.api.sampler.Sampler; import speiger.src.coreengine.platform.graphics.api.sampler.Sampler;
import speiger.src.coreengine.platform.graphics.api.sampler.SamplerSettings; import speiger.src.coreengine.platform.graphics.api.sampler.SamplerSettings;
@@ -28,6 +29,7 @@ public interface GraphicsDevice {
Texture createTexture(TextureSettings data, int width, int height); Texture createTexture(TextureSettings data, int width, int height);
Sampler createSampler(SamplerSettings settings); Sampler createSampler(SamplerSettings settings);
Mesh createMesh(Mesh.Builder builder); Mesh createMesh(Mesh.Builder builder);
DynamicMesh createDynamicMesh(DynamicMesh.Builder builder);
TimeQueryPool createQueryPool(int size); TimeQueryPool createQueryPool(int size);
void preloadPipelines(List<ShaderPipeline> graphics, List<ComputePipeline> compute); void preloadPipelines(List<ShaderPipeline> graphics, List<ComputePipeline> compute);
} }
@@ -0,0 +1,111 @@
package speiger.src.coreengine.platform.graphics.api.mesh;
import java.util.Objects;
import java.util.OptionalInt;
import org.jspecify.annotations.Nullable;
import speiger.src.collections.ints.collections.IntIterable;
import speiger.src.collections.ints.maps.impl.hash.Int2ObjectOpenHashMap;
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap;
import speiger.src.coreengine.platform.graphics.api.buffer.VertexBuffer;
import speiger.src.coreengine.platform.graphics.api.buffer.states.BufferState;
import speiger.src.coreengine.platform.graphics.api.buffer.states.IndeciesType;
import speiger.src.coreengine.platform.graphics.api.utils.GraphicsResource;
public abstract class DynamicMesh implements GraphicsResource {
protected Int2ObjectMap<VertexBuffer> buffers;
@Nullable
protected VertexBuffer indeciesBuffer;
@Nullable
protected IndeciesType indeciesType;
public DynamicMesh(Int2ObjectMap<VertexBuffer> buffers, @Nullable VertexBuffer indeciesBuffer, @Nullable IndeciesType type) {
this.buffers = buffers;
this.indeciesBuffer = indeciesBuffer;
}
public abstract void closeWithBuffers();
@Nullable
public VertexBuffer indeciesBuffer() {
return indeciesBuffer;
}
@Nullable
public IndeciesType indeciesType() {
return indeciesType;
}
public VertexBuffer buffer(int binding) {
return buffers.get(binding);
}
public DynamicMesh indexBuffer(@Nullable VertexBuffer indexBuffer, @Nullable IndeciesType indeciesType) {
this.indeciesBuffer = indexBuffer;
this.indeciesType = indeciesType;
return this;
}
public DynamicMesh buffer(int binding, @Nullable VertexBuffer buffer) {
buffers.put(binding, buffer);
return this;
}
public IntIterable bindings() {
return buffers.keySet().unmodifiable();
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
Int2ObjectMap<BufferState> allocateState = new Int2ObjectOpenHashMap<>();
IndeciesType type = IndeciesType.INT;
OptionalInt preallocated = OptionalInt.empty();
boolean autoGenerate;
private Builder() {}
public Builder autogenerate() {
autoGenerate = true;
return this;
}
public Builder preallocate(int bytes) {
preallocated = OptionalInt.of(bytes);
return this;
}
public Builder indecies(IndeciesType type) {
type = Objects.requireNonNull(type);
return this;
}
public Builder bufferState(int index, BufferState state) {
allocateState.put(index, state);
return this;
}
public boolean isAutogenerated() {
return autoGenerate;
}
public IndeciesType indecies() {
return type;
}
public OptionalInt preAllocated() {
return preallocated;
}
public Int2ObjectMap<BufferState> buffers() {
return allocateState.unmodifiable();
}
public BufferState getIndeciesBuffer() {
return allocateState.getOrDefault(-1, BufferState.STATIC_DRAW);
}
}
}
@@ -16,25 +16,30 @@ import speiger.src.coreengine.platform.graphics.api.vertex.VertexLayout;
public abstract class Mesh implements GraphicsResource { public abstract class Mesh implements GraphicsResource {
protected final Int2ObjectMap<LayoutInfo> layouts; protected final Int2ObjectMap<LayoutInfo> layouts;
protected Int2ObjectMap<VertexBuffer> buffers; protected Int2ObjectMap<VertexBuffer> buffers;
@Nullable
protected VertexBuffer indeciesBuffer; protected VertexBuffer indeciesBuffer;
@Nullable
protected IndeciesType indeciesType; protected IndeciesType indeciesType;
public Mesh(Int2ObjectMap<LayoutInfo> layouts, Int2ObjectMap<VertexBuffer> buffers, @Nullable VertexBuffer indeciesBuffer, @Nullable IndeciesType type) { public Mesh(Int2ObjectMap<LayoutInfo> layouts, Int2ObjectMap<VertexBuffer> buffers, @Nullable VertexBuffer indeciesBuffer, @Nullable IndeciesType type) {
this.layouts = layouts; this.layouts = layouts;
this.buffers = buffers; this.buffers = buffers;
this.indeciesBuffer = indeciesBuffer; this.indeciesBuffer = indeciesBuffer;
this.indeciesType = type;
} }
protected abstract void closeWithBuffers(); public abstract void closeWithBuffers();
public Int2ObjectMap<LayoutInfo> layouts() { public Int2ObjectMap<LayoutInfo> layouts() {
return layouts; return layouts;
} }
@Nullable
public VertexBuffer indeciesBuffer() { public VertexBuffer indeciesBuffer() {
return indeciesBuffer; return indeciesBuffer;
} }
@Nullable
public IndeciesType indeciesType() { public IndeciesType indeciesType() {
return indeciesType; return indeciesType;
} }
@@ -43,13 +48,13 @@ public abstract class Mesh implements GraphicsResource {
return buffers.get(binding); return buffers.get(binding);
} }
public Mesh indexBuffer(VertexBuffer indexBuffer, IndeciesType indeciesType) { public Mesh indexBuffer(@Nullable VertexBuffer indexBuffer, @Nullable IndeciesType indeciesType) {
this.indeciesBuffer = indexBuffer; this.indeciesBuffer = indexBuffer;
this.indeciesType = indeciesType; this.indeciesType = indeciesType;
return this; return this;
} }
public Mesh buffer(int binding, VertexBuffer buffer) { public Mesh buffer(int binding, @Nullable VertexBuffer buffer) {
buffers.put(binding, buffer); buffers.put(binding, buffer);
return this; return this;
} }
@@ -59,7 +64,7 @@ public abstract class Mesh implements GraphicsResource {
} }
public static class Builder { public static class Builder {
Int2ObjectMap<BufferState> allocateState = new Int2ObjectOpenHashMap<BufferState>().setDefaultReturnValue(BufferState.STATIC_READ); Int2ObjectMap<BufferState> allocateState = new Int2ObjectOpenHashMap<BufferState>().setDefaultReturnValue(BufferState.STATIC_DRAW);
Int2ObjectMap<LayoutInfo> layouts = Int2ObjectMap.builder().linkedMap(); Int2ObjectMap<LayoutInfo> layouts = Int2ObjectMap.builder().linkedMap();
IndeciesType type = IndeciesType.INT; IndeciesType type = IndeciesType.INT;
OptionalInt preallocated = OptionalInt.empty(); OptionalInt preallocated = OptionalInt.empty();
@@ -46,5 +46,7 @@ public interface IVertexBuffer extends IVertexBuilder {
throw new IllegalStateException("Expected State [Usage="+current.usage()+", Size="+current.size()+", Type="+current.type()+"], wasn't present. Actual State [Usage="+usage+", Size="+size+", Type="+type+"]"); throw new IllegalStateException("Expected State [Usage="+current.usage()+", Size="+current.size()+", Type="+current.type()+"], wasn't present. Actual State [Usage="+usage+", Size="+size+", Type="+type+"]");
} }
public record BufferResult(DrawMode mode, int startVertex, int endVertex) {} public record BufferResult(DrawMode mode, int startVertex, int endVertex) {
public int vertexCount() { return endVertex - startVertex; }
}
} }
@@ -1,14 +1,17 @@
package speiger.src.coreengine.platform.graphics.api.vertex.builder; package speiger.src.coreengine.platform.graphics.api.vertex.builder;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Objects;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
import speiger.src.coreengine.math.MathUtils;
import speiger.src.coreengine.platform.graphics.api.shader.states.DrawMode; import speiger.src.coreengine.platform.graphics.api.shader.states.DrawMode;
import speiger.src.coreengine.platform.graphics.api.vertex.VertexLayout; import speiger.src.coreengine.platform.graphics.api.vertex.VertexLayout;
import speiger.src.coreengine.platform.graphics.api.vertex.VertexLayout.Element; import speiger.src.coreengine.platform.graphics.api.vertex.VertexLayout.Element;
public class VertexBuilder implements IVertexBuffer, AutoCloseable { public class VertexBuilder implements IVertexBuffer, AutoCloseable {
boolean autoAlign;
boolean noFree = false; boolean noFree = false;
DrawMode mode; DrawMode mode;
ByteBuffer buffer; ByteBuffer buffer;
@@ -29,6 +32,11 @@ public class VertexBuilder implements IVertexBuffer, AutoCloseable {
this.buffer = buffer; this.buffer = buffer;
} }
public VertexBuilder withAutoAlign() {
this.autoAlign = true;
return this;
}
public VertexBuilder withoutFree() { public VertexBuilder withoutFree() {
noFree = true; noFree = true;
return this; return this;
@@ -36,8 +44,12 @@ public class VertexBuilder implements IVertexBuffer, AutoCloseable {
public VertexBuilder start(DrawMode mode, VertexLayout layout) { public VertexBuilder start(DrawMode mode, VertexLayout layout) {
if(index != 0) throw new IllegalStateException("Can't change Layout mid Writing"); if(index != 0) throw new IllegalStateException("Can't change Layout mid Writing");
this.mode = mode; if(autoAlign && this.layout != layout) {
this.layout = layout; totalStoredBytes = MathUtils.roundAlign(totalStoredBytes, layout.bytes());
ensureCapacity(totalStoredBytes + (layout.bytes() * mode.primitiveLength()));
}
this.mode = Objects.requireNonNull(mode);
this.layout = Objects.requireNonNull(layout);
return this; return this;
} }
@@ -55,6 +67,8 @@ public class VertexBuilder implements IVertexBuffer, AutoCloseable {
public boolean is(DrawMode mode, VertexLayout layout) { return this.mode == mode && this.layout == layout; } public boolean is(DrawMode mode, VertexLayout layout) { return this.mode == mode && this.layout == layout; }
public DrawMode mode() { return mode; } public DrawMode mode() { return mode; }
public VertexLayout getFormat() { return layout; } public VertexLayout getFormat() { return layout; }
public long memoryPointer() { return MemoryUtil.memAddress(buffer); }
public int totalStoredBytes() { return totalStoredBytes; }
@Override @Override
public void close() { public void close() {
@@ -70,6 +84,12 @@ public class VertexBuilder implements IVertexBuffer, AutoCloseable {
@Override @Override
public BufferResult finish() { public BufferResult finish() {
if(autoAlign) {
int count = vertecies - lastVertecies;
lastVertecies = vertecies;
int vertecies = totalStoredBytes / layout.bytes();
return new BufferResult(mode, vertecies - count, vertecies);
}
BufferResult result = new BufferResult(mode, lastVertecies, vertecies); BufferResult result = new BufferResult(mode, lastVertecies, vertecies);
lastVertecies = vertecies; lastVertecies = vertecies;
return result; return result;
@@ -45,23 +45,26 @@ import speiger.src.coreengine.platform.graphics.api.compute.ComputePipeline;
import speiger.src.coreengine.platform.graphics.api.core.GraphicsCommandBuffer; import speiger.src.coreengine.platform.graphics.api.core.GraphicsCommandBuffer;
import speiger.src.coreengine.platform.graphics.api.core.GraphicsDevice; import speiger.src.coreengine.platform.graphics.api.core.GraphicsDevice;
import speiger.src.coreengine.platform.graphics.api.core.TimeQueryPool; import speiger.src.coreengine.platform.graphics.api.core.TimeQueryPool;
import speiger.src.coreengine.platform.graphics.api.mesh.DynamicMesh;
import speiger.src.coreengine.platform.graphics.api.mesh.Mesh; import speiger.src.coreengine.platform.graphics.api.mesh.Mesh;
import speiger.src.coreengine.platform.graphics.api.mesh.Mesh.LayoutInfo; import speiger.src.coreengine.platform.graphics.api.mesh.Mesh.LayoutInfo;
import speiger.src.coreengine.platform.graphics.api.sampler.SamplerSettings; import speiger.src.coreengine.platform.graphics.api.sampler.SamplerSettings;
import speiger.src.coreengine.platform.graphics.api.shader.ShaderPipeline; import speiger.src.coreengine.platform.graphics.api.shader.ShaderPipeline;
import speiger.src.coreengine.platform.graphics.api.shader.VertexBinding; import speiger.src.coreengine.platform.graphics.api.shader.VertexBinding;
import speiger.src.coreengine.platform.graphics.api.shader.states.ShaderType;
import speiger.src.coreengine.platform.graphics.api.shader.states.Bindings.BufferBinding; import speiger.src.coreengine.platform.graphics.api.shader.states.Bindings.BufferBinding;
import speiger.src.coreengine.platform.graphics.api.shader.states.Bindings.BufferMode; import speiger.src.coreengine.platform.graphics.api.shader.states.Bindings.BufferMode;
import speiger.src.coreengine.platform.graphics.api.shader.states.Bindings.TextureBinding; import speiger.src.coreengine.platform.graphics.api.shader.states.Bindings.TextureBinding;
import speiger.src.coreengine.platform.graphics.api.shader.states.Bindings.TextureMode; import speiger.src.coreengine.platform.graphics.api.shader.states.Bindings.TextureMode;
import speiger.src.coreengine.platform.graphics.api.shader.states.ShaderType;
import speiger.src.coreengine.platform.graphics.api.texture.TextureSettings; import speiger.src.coreengine.platform.graphics.api.texture.TextureSettings;
import speiger.src.coreengine.platform.graphics.api.texture.states.SwizzleMask; import speiger.src.coreengine.platform.graphics.api.texture.states.SwizzleMask;
import speiger.src.coreengine.platform.graphics.api.texture.states.TextureType; import speiger.src.coreengine.platform.graphics.api.texture.states.TextureType;
import speiger.src.coreengine.platform.graphics.api.utils.ExecutionType; import speiger.src.coreengine.platform.graphics.api.utils.ExecutionType;
import speiger.src.coreengine.platform.graphics.api.vertex.VertexLayout.Element; import speiger.src.coreengine.platform.graphics.api.vertex.VertexLayout.Element;
import speiger.src.coreengine.platform.graphics.opengl.buffer.GLVertexBuffer; import speiger.src.coreengine.platform.graphics.opengl.buffer.GLVertexBuffer;
import speiger.src.coreengine.platform.graphics.opengl.mesh.GLDynamicMesh;
import speiger.src.coreengine.platform.graphics.opengl.mesh.GLMesh; import speiger.src.coreengine.platform.graphics.opengl.mesh.GLMesh;
import speiger.src.coreengine.platform.graphics.opengl.mesh.VertexArrayCache;
import speiger.src.coreengine.platform.graphics.opengl.sampler.GLSampler; import speiger.src.coreengine.platform.graphics.opengl.sampler.GLSampler;
import speiger.src.coreengine.platform.graphics.opengl.shader.ShaderCache; import speiger.src.coreengine.platform.graphics.opengl.shader.ShaderCache;
import speiger.src.coreengine.platform.graphics.opengl.shader.ShaderInstance; import speiger.src.coreengine.platform.graphics.opengl.shader.ShaderInstance;
@@ -78,6 +81,7 @@ public class GLGraphicsDevice implements GraphicsDevice {
static final Map<ID, String> IMPORTS = Object2ObjectMap.builder().<ID, String>map().synchronize(); static final Map<ID, String> IMPORTS = Object2ObjectMap.builder().<ID, String>map().synchronize();
Object2IntMap<ID> shaderCache = new Object2IntOpenHashMap<>(); Object2IntMap<ID> shaderCache = new Object2IntOpenHashMap<>();
Object2ObjectMap<ID, ShaderInstance> programCache = new Object2ObjectOpenHashMap<>(); Object2ObjectMap<ID, ShaderInstance> programCache = new Object2ObjectOpenHashMap<>();
VertexArrayCache vertexCache = new VertexArrayCache();
AssetManager manager; AssetManager manager;
GLCapabilities capabilities; GLCapabilities capabilities;
GLStates states = new GLStates(); GLStates states = new GLStates();
@@ -167,6 +171,22 @@ public class GLGraphicsDevice implements GraphicsDevice {
return new GLMesh(builder.info(), new Int2ObjectOpenHashMap<>(), null, null, false); return new GLMesh(builder.info(), new Int2ObjectOpenHashMap<>(), null, null, false);
} }
@Override
public DynamicMesh createDynamicMesh(DynamicMesh.Builder builder) {
if(builder.isAutogenerated()) {
OptionalInt preAllocation = builder.preAllocated();
Int2ObjectMap<VertexBuffer> buffers = new Int2ObjectLinkedOpenHashMap<>();
for(Int2ObjectMap.Entry<BufferState> entry : builder.buffers().int2ObjectEntrySet()) {
int key = entry.getIntKey();
if(key == -1) continue;
buffers.put(key, preAllocation.isPresent() ? createBuffer(BufferType.ARRAY_BUFFER, entry.getValue(), preAllocation.getAsInt()) : createBuffer(BufferType.ARRAY_BUFFER, entry.getValue()));
}
return new GLDynamicMesh(buffers, createBuffer(BufferType.ELEMENT_BUFFER, builder.getIndeciesBuffer()), builder.indecies(), true);
}
return new GLDynamicMesh(new Int2ObjectOpenHashMap<>(), null, null, false);
}
@Override @Override
public TimeQueryPool createQueryPool(int size) { public TimeQueryPool createQueryPool(int size) {
return new GLTimeQueryPool(size); return new GLTimeQueryPool(size);
@@ -11,6 +11,7 @@ import org.lwjgl.opengl.GL45;
import speiger.src.coreengine.platform.graphics.api.buffer.VertexBuffer; import speiger.src.coreengine.platform.graphics.api.buffer.VertexBuffer;
import speiger.src.coreengine.platform.graphics.api.buffer.states.BufferType; import speiger.src.coreengine.platform.graphics.api.buffer.states.BufferType;
import speiger.src.coreengine.platform.graphics.api.core.GraphicsCommandBuffer; import speiger.src.coreengine.platform.graphics.api.core.GraphicsCommandBuffer;
import speiger.src.coreengine.platform.graphics.api.mesh.DynamicMesh;
import speiger.src.coreengine.platform.graphics.api.mesh.Mesh; import speiger.src.coreengine.platform.graphics.api.mesh.Mesh;
import speiger.src.coreengine.platform.graphics.api.shader.ColorTarget; import speiger.src.coreengine.platform.graphics.api.shader.ColorTarget;
import speiger.src.coreengine.platform.graphics.api.shader.DepthTarget; import speiger.src.coreengine.platform.graphics.api.shader.DepthTarget;
@@ -21,6 +22,7 @@ import speiger.src.coreengine.platform.graphics.api.utils.AccessType;
import speiger.src.coreengine.platform.graphics.api.utils.SampledTexture; import speiger.src.coreengine.platform.graphics.api.utils.SampledTexture;
import speiger.src.coreengine.platform.graphics.api.utils.ScissorsManager; import speiger.src.coreengine.platform.graphics.api.utils.ScissorsManager;
import speiger.src.coreengine.platform.graphics.opengl.buffer.GLVertexBuffer; import speiger.src.coreengine.platform.graphics.opengl.buffer.GLVertexBuffer;
import speiger.src.coreengine.platform.graphics.opengl.mesh.GLDynamicMesh;
import speiger.src.coreengine.platform.graphics.opengl.mesh.GLMesh; import speiger.src.coreengine.platform.graphics.opengl.mesh.GLMesh;
import speiger.src.coreengine.platform.graphics.opengl.sampler.GLSampler; import speiger.src.coreengine.platform.graphics.opengl.sampler.GLSampler;
import speiger.src.coreengine.platform.graphics.opengl.shader.ShaderInstance; import speiger.src.coreengine.platform.graphics.opengl.shader.ShaderInstance;
@@ -39,6 +41,7 @@ public class GLImmidateCommandBuffer implements GraphicsCommandBuffer {
ShaderPipeline pipeline; ShaderPipeline pipeline;
ShaderInstance shader; ShaderInstance shader;
GLMesh mesh; GLMesh mesh;
GLDynamicMesh dynamicMesh;
public GLImmidateCommandBuffer(GLGraphicsDevice device) { public GLImmidateCommandBuffer(GLGraphicsDevice device) {
this.device = device; this.device = device;
@@ -102,6 +105,7 @@ public class GLImmidateCommandBuffer implements GraphicsCommandBuffer {
ColorTarget color = pipeline.colorTarget(); ColorTarget color = pipeline.colorTarget();
color.blend().ifPresentOrElse(states.blend::enableSet, states.blend::disable); color.blend().ifPresentOrElse(states.blend::enableSet, states.blend::disable);
states.setColorMask(color.writeMask()); states.setColorMask(color.writeMask());
if(dynamicMesh != null) bindDynamicMesh();
recorded++; recorded++;
return this; return this;
} }
@@ -113,10 +117,27 @@ public class GLImmidateCommandBuffer implements GraphicsCommandBuffer {
if(this.mesh == mesh) return this; if(this.mesh == mesh) return this;
this.mesh = (GLMesh)mesh; this.mesh = (GLMesh)mesh;
GL30.glBindVertexArray(this.mesh.id()); GL30.glBindVertexArray(this.mesh.id());
this.dynamicMesh = null;
recorded++; recorded++;
return this; return this;
} }
@Override
public GraphicsCommandBuffer mesh(DynamicMesh mesh) {
Objects.requireNonNull(mesh);
ensureDrawing();
if(this.dynamicMesh == mesh) return this;
this.dynamicMesh = (GLDynamicMesh)mesh;
bindDynamicMesh();
this.mesh = null;
return this;
}
protected void bindDynamicMesh() {
if(pipeline == null || dynamicMesh == null) return;
device.vertexCache.apply(pipeline, dynamicMesh);
}
@Override @Override
public GraphicsCommandBuffer texture(int binding, SampledTexture texture) { public GraphicsCommandBuffer texture(int binding, SampledTexture texture) {
Objects.requireNonNull(texture); Objects.requireNonNull(texture);
@@ -15,6 +15,7 @@ import speiger.src.coreengine.platform.graphics.api.buffer.VertexBuffer;
import speiger.src.coreengine.platform.graphics.api.buffer.states.BufferType; import speiger.src.coreengine.platform.graphics.api.buffer.states.BufferType;
import speiger.src.coreengine.platform.graphics.api.buffer.states.IndeciesType; import speiger.src.coreengine.platform.graphics.api.buffer.states.IndeciesType;
import speiger.src.coreengine.platform.graphics.api.core.GraphicsCommandBuffer; import speiger.src.coreengine.platform.graphics.api.core.GraphicsCommandBuffer;
import speiger.src.coreengine.platform.graphics.api.mesh.DynamicMesh;
import speiger.src.coreengine.platform.graphics.api.mesh.Mesh; import speiger.src.coreengine.platform.graphics.api.mesh.Mesh;
import speiger.src.coreengine.platform.graphics.api.shader.ColorTarget; import speiger.src.coreengine.platform.graphics.api.shader.ColorTarget;
import speiger.src.coreengine.platform.graphics.api.shader.DepthTarget; import speiger.src.coreengine.platform.graphics.api.shader.DepthTarget;
@@ -27,7 +28,9 @@ import speiger.src.coreengine.platform.graphics.api.utils.SampledTexture;
import speiger.src.coreengine.platform.graphics.api.utils.ScissorsManager; import speiger.src.coreengine.platform.graphics.api.utils.ScissorsManager;
import speiger.src.coreengine.platform.graphics.api.utils.ScissorsManager.NoOp; import speiger.src.coreengine.platform.graphics.api.utils.ScissorsManager.NoOp;
import speiger.src.coreengine.platform.graphics.opengl.buffer.GLVertexBuffer; import speiger.src.coreengine.platform.graphics.opengl.buffer.GLVertexBuffer;
import speiger.src.coreengine.platform.graphics.opengl.mesh.GLDynamicMesh;
import speiger.src.coreengine.platform.graphics.opengl.mesh.GLMesh; import speiger.src.coreengine.platform.graphics.opengl.mesh.GLMesh;
import speiger.src.coreengine.platform.graphics.opengl.mesh.VertexArrayCache;
import speiger.src.coreengine.platform.graphics.opengl.sampler.GLSampler; import speiger.src.coreengine.platform.graphics.opengl.sampler.GLSampler;
import speiger.src.coreengine.platform.graphics.opengl.shader.ShaderInstance; import speiger.src.coreengine.platform.graphics.opengl.shader.ShaderInstance;
import speiger.src.coreengine.platform.graphics.opengl.texture.GLTexture; import speiger.src.coreengine.platform.graphics.opengl.texture.GLTexture;
@@ -44,7 +47,7 @@ public class GLRecordingCommandBuffer implements GraphicsCommandBuffer {
ShaderPipeline pipeline; ShaderPipeline pipeline;
ShaderInstance shader; ShaderInstance shader;
GLMesh mesh; GLMesh mesh;
GLDynamicMesh dynamicMesh;
public GLRecordingCommandBuffer(GLGraphicsDevice device) { public GLRecordingCommandBuffer(GLGraphicsDevice device) {
this.device = device; this.device = device;
@@ -95,7 +98,7 @@ public class GLRecordingCommandBuffer implements GraphicsCommandBuffer {
//TODO implement logging //TODO implement logging
return this; return this;
} }
tasks.add(new SetPipeline(pipeline, shader, device.states)); tasks.add(new SetPipeline(pipeline, shader, dynamicMesh, device.states, device.vertexCache));
return this; return this;
} }
@@ -106,6 +109,18 @@ public class GLRecordingCommandBuffer implements GraphicsCommandBuffer {
if(this.mesh == mesh) return this; if(this.mesh == mesh) return this;
this.mesh = (GLMesh)mesh; this.mesh = (GLMesh)mesh;
this.tasks.add(new SetMesh(this.mesh)); this.tasks.add(new SetMesh(this.mesh));
this.dynamicMesh = null;
return this;
}
@Override
public GraphicsCommandBuffer mesh(DynamicMesh mesh) {
Objects.requireNonNull(mesh);
ensureDrawing();
if(this.dynamicMesh == mesh) return this;
this.dynamicMesh = (GLDynamicMesh)mesh;
this.tasks.add(new SetDynamicMesh(dynamicMesh, pipeline, device.vertexCache));
this.mesh = null;
return this; return this;
} }
@@ -238,7 +253,7 @@ public class GLRecordingCommandBuffer implements GraphicsCommandBuffer {
public void run() { states.scissors.set(area); } public void run() { states.scissors.set(area); }
} }
public record SetPipeline(ShaderPipeline pipeline, ShaderInstance instance, GLStates states) implements Runnable { public record SetPipeline(ShaderPipeline pipeline, ShaderInstance instance, GLDynamicMesh mesh, GLStates states, VertexArrayCache cache) implements Runnable {
@Override @Override
public void run() { public void run() {
states.shaders.bind(instance); states.shaders.bind(instance);
@@ -255,6 +270,7 @@ public class GLRecordingCommandBuffer implements GraphicsCommandBuffer {
ColorTarget color = pipeline.colorTarget(); ColorTarget color = pipeline.colorTarget();
color.blend().ifPresentOrElse(states.blend::enableSet, states.blend::disable); color.blend().ifPresentOrElse(states.blend::enableSet, states.blend::disable);
states.setColorMask(color.writeMask()); states.setColorMask(color.writeMask());
if(mesh != null) cache.apply(pipeline, mesh);
} }
} }
@@ -263,6 +279,11 @@ public class GLRecordingCommandBuffer implements GraphicsCommandBuffer {
public void run() { GL30.glBindVertexArray(mesh.id()); } public void run() { GL30.glBindVertexArray(mesh.id()); }
} }
public record SetDynamicMesh(GLDynamicMesh mesh, ShaderPipeline pipeline, VertexArrayCache cache) implements Runnable {
@Override
public void run() { cache.apply(pipeline, mesh); }
}
public record SetTexture(int binding, GLTexture texture, GLSampler sampler, GLStates states) implements Runnable { public record SetTexture(int binding, GLTexture texture, GLSampler sampler, GLStates states) implements Runnable {
@Override @Override
public void run() { public void run() {
@@ -0,0 +1,39 @@
package speiger.src.coreengine.platform.graphics.opengl.mesh;
import org.jspecify.annotations.Nullable;
import speiger.src.collections.ints.maps.interfaces.Int2ObjectMap;
import speiger.src.coreengine.platform.graphics.api.buffer.VertexBuffer;
import speiger.src.coreengine.platform.graphics.api.buffer.states.IndeciesType;
import speiger.src.coreengine.platform.graphics.api.mesh.DynamicMesh;
public class GLDynamicMesh extends DynamicMesh {
boolean managed;
boolean invalid = false;
public GLDynamicMesh(Int2ObjectMap<VertexBuffer> buffers, @Nullable VertexBuffer indeciesBuffer, @Nullable IndeciesType type, boolean managed) {
super(buffers, indeciesBuffer, type);
this.managed = managed;
}
@Override
public boolean isRemoved() { return invalid; }
@Override
public void remove() {
if(invalid) return;
invalid = true;
if(!managed) return;
if(indeciesBuffer != null) indeciesBuffer.remove();
buffers.values().forEach(VertexBuffer::close);
}
@Override
public void closeWithBuffers() {
boolean wasManaged = managed;
managed = true;
close();
managed = wasManaged;
}
}
@@ -86,7 +86,7 @@ public class GLMesh extends Mesh {
} }
@Override @Override
protected void closeWithBuffers() { public void closeWithBuffers() {
boolean wasManaged = managed; boolean wasManaged = managed;
managed = true; managed = true;
close(); close();
@@ -0,0 +1,83 @@
package speiger.src.coreengine.platform.graphics.opengl.mesh;
import java.util.Map;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL45;
import speiger.src.collections.ints.maps.impl.hash.Int2IntOpenHashMap;
import speiger.src.collections.ints.maps.interfaces.Int2IntMap;
import speiger.src.collections.objects.maps.impl.hash.Object2ObjectOpenHashMap;
import speiger.src.coreengine.platform.graphics.api.mesh.DynamicMesh;
import speiger.src.coreengine.platform.graphics.api.shader.ShaderPipeline;
import speiger.src.coreengine.platform.graphics.api.shader.VertexBinding;
import speiger.src.coreengine.platform.graphics.api.vertex.VertexLayout;
import speiger.src.coreengine.platform.graphics.api.vertex.VertexLayout.Element;
import speiger.src.coreengine.platform.graphics.opengl.buffer.GLVertexBuffer;
import speiger.src.coreengine.platform.graphics.opengl.utils.GLUtils;
public class VertexArrayCache {
Map<ShaderPipeline, CacheEntry> cache = new Object2ObjectOpenHashMap<>();
public void apply(ShaderPipeline pipeline, GLDynamicMesh mesh) {
CacheEntry entry = cache.get(pipeline);
if(entry == null) {
entry = new CacheEntry(GL45.glCreateVertexArrays(), toIds(mesh));
cache.put(pipeline, entry);
apply(entry.vao, pipeline, mesh);
GL30.glBindVertexArray(entry.vao);
return;
}
Int2IntMap newIds = toIds(mesh);
if(entry.existing.equals(newIds)) {
GL30.glBindVertexArray(entry.vao);
return;
}
entry.existing = newIds;
apply(entry.vao, pipeline, mesh);
GL30.glBindVertexArray(entry.vao);
}
private void apply(int vao, ShaderPipeline pipeline, DynamicMesh mesh) {
for(VertexBinding binding : pipeline.attributes()) {
bindBuffer(vao, binding.bindingIndex(), binding, mesh);
}
if(mesh.indeciesBuffer() != null) {
mesh.indeciesBuffer().bind();
GL45.glVertexArrayElementBuffer(vao, ((GLVertexBuffer)mesh.indeciesBuffer()).id());
}
}
private void bindBuffer(int vao, int binding, VertexBinding info, DynamicMesh mesh) {
VertexLayout layout = info.layout();
GLVertexBuffer buffer = (GLVertexBuffer)mesh.buffer(binding);
buffer.bind();
GL45.glVertexArrayVertexBuffer(vao, binding, buffer.id(), 0, layout.bytes());
if(info.instanceOffset() > 0) GL45.glVertexArrayBindingDivisor(vao, binding, info.instanceOffset());
int index = 0;
for(Element element : layout) {
GL45.glVertexArrayAttribFormat(vao, element.index(), element.size(), GLUtils.toGL(element.type()), element.normalized(), layout.offset(index++));
GL45.glVertexArrayAttribBinding(vao, element.index(), binding);
GL45.glEnableVertexArrayAttrib(vao, element.index());
}
}
private Int2IntMap toIds(GLDynamicMesh mesh) {
Int2IntMap existing = new Int2IntOpenHashMap();
for(int entry : mesh.bindings()) {
existing.put(entry, ((GLVertexBuffer)mesh.buffer(entry)).id());
}
return existing;
}
private static class CacheEntry {
Int2IntMap existing;
int vao;
public CacheEntry(int vao, Int2IntMap existing) {
this.existing = existing;
this.vao = vao;
}
}
}
@@ -1,12 +1,18 @@
package speiger.src.coreengine.ui.gui.renderer; package speiger.src.coreengine.ui.gui.renderer;
import org.jspecify.annotations.Nullable;
import speiger.src.coreengine.math.vector.matrix.Matrix4f; import speiger.src.coreengine.math.vector.matrix.Matrix4f;
import speiger.src.coreengine.platform.graphics.api.core.GraphicsCommandBuffer; import speiger.src.coreengine.platform.graphics.api.core.GraphicsCommandBuffer;
import speiger.src.coreengine.platform.graphics.api.shader.ShaderPipeline; import speiger.src.coreengine.platform.graphics.api.shader.ShaderPipeline;
import speiger.src.coreengine.platform.graphics.api.utils.SampledTexture;
import speiger.src.coreengine.platform.graphics.api.vertex.builder.IVertexBuilder; import speiger.src.coreengine.platform.graphics.api.vertex.builder.IVertexBuilder;
public interface UIDrawer { public interface UIDrawer {
ShaderPipeline pipeline(); ShaderPipeline pipeline();
@Nullable
SampledTexture texture();
void buildVertecies(IVertexBuilder builder, Matrix4f matrix); void buildVertecies(IVertexBuilder builder, Matrix4f matrix);
boolean requiresSpecialArgs();
void setupExtra(GraphicsCommandBuffer buffer); void setupExtra(GraphicsCommandBuffer buffer);
} }
@@ -1,20 +1,27 @@
package speiger.src.coreengine.ui.gui.renderer; package speiger.src.coreengine.ui.gui.renderer;
import java.util.Comparator;
import java.util.List; import java.util.List;
import org.jspecify.annotations.Nullable;
import speiger.src.collections.ints.collections.IntStack; import speiger.src.collections.ints.collections.IntStack;
import speiger.src.collections.ints.lists.IntArrayList; import speiger.src.collections.ints.lists.IntArrayList;
import speiger.src.collections.objects.lists.ObjectArrayList; import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.utils.Stack; import speiger.src.collections.utils.Stack;
import speiger.src.coreengine.math.MathUtils;
import speiger.src.coreengine.math.bits.BitUtil; import speiger.src.coreengine.math.bits.BitUtil;
import speiger.src.coreengine.math.vector.ints.Vec4i;
import speiger.src.coreengine.math.vector.matrix.Matrix4fStack; import speiger.src.coreengine.math.vector.matrix.Matrix4fStack;
import speiger.src.coreengine.math.vector.quaternion.Quaternion; import speiger.src.coreengine.math.vector.quaternion.Quaternion;
import speiger.src.coreengine.platform.graphics.api.buffer.states.BufferState;
import speiger.src.coreengine.platform.graphics.api.core.GraphicsCommandBuffer;
import speiger.src.coreengine.platform.graphics.api.core.GraphicsDevice; import speiger.src.coreengine.platform.graphics.api.core.GraphicsDevice;
import speiger.src.coreengine.platform.graphics.api.mesh.DynamicMesh;
import speiger.src.coreengine.platform.graphics.api.shader.ShaderPipeline; import speiger.src.coreengine.platform.graphics.api.shader.ShaderPipeline;
import speiger.src.coreengine.platform.graphics.api.utils.BufferOwner;
import speiger.src.coreengine.platform.graphics.api.utils.ExecutionType;
import speiger.src.coreengine.platform.graphics.api.utils.SampledTexture; import speiger.src.coreengine.platform.graphics.api.utils.SampledTexture;
import speiger.src.coreengine.platform.graphics.api.utils.ScissorsManager; import speiger.src.coreengine.platform.graphics.api.utils.ScissorsManager;
import speiger.src.coreengine.platform.graphics.api.utils.ScissorsManager.IScissorsHandler;
import speiger.src.coreengine.platform.graphics.api.vertex.builder.IVertexBuffer.BufferResult; import speiger.src.coreengine.platform.graphics.api.vertex.builder.IVertexBuffer.BufferResult;
import speiger.src.coreengine.platform.graphics.api.vertex.builder.IVertexBuilder; import speiger.src.coreengine.platform.graphics.api.vertex.builder.IVertexBuilder;
import speiger.src.coreengine.platform.graphics.api.vertex.builder.VertexBuilder; import speiger.src.coreengine.platform.graphics.api.vertex.builder.VertexBuilder;
@@ -33,10 +40,55 @@ public class UIRenderer implements IUIRenderer {
int currentComponent = 0; int currentComponent = 0;
ShaderPipeline currentPipeline; ShaderPipeline currentPipeline;
SampledTexture currentTexture; SampledTexture currentTexture;
UIDrawer lastDrawer;
ScissorsManager clippingManager = new ScissorsManager(64, ScissorsManager.NoOp.INSTANCE); ScissorsManager clippingManager = new ScissorsManager(64, ScissorsManager.NoOp.INSTANCE);
DynamicMesh mesh;
public UIRenderer(GraphicsDevice device) { public UIRenderer(GraphicsDevice device) {
this.device = device; this.device = device;
builder.withAutoAlign();
mesh = device.createDynamicMesh(DynamicMesh.builder().autogenerate().bufferState(0, BufferState.STREAM_DRAW));
}
public void endFrame() {
draws.sort(Comparator.comparingInt(UIDraw::currentLayer).thenComparingInt(UIDraw::componentIndex));
mesh.buffer(0).set(builder.memoryPointer(), builder.totalStoredBytes(), BufferOwner.KEPT);
GraphicsCommandBuffer buffer = device.createCommandBuffer(ExecutionType.IMMEDIATE);
buffer.begin();
buffer.mesh(mesh);
for(int i = 0,m=draws.size();i<m;i++) {
UIDraw draw = draws.get(i);
BufferResult result = draw.result();
if(draw.drawer != null) {
draw.drawer.setupExtra(buffer);
buffer.mesh(mesh);
}
if(draw.texture() != null) buffer.texture(0, draw.texture());
buffer.pipeline(draw.pipeline());
boolean pop = false;
Vec4i scissors = draw.scissors();
if(scissors != Vec4i.MINUS_ONE) {
pop = true;
buffer.pushScissors(scissors.x(), scissors.y(), scissors.z() - scissors.x(), scissors.w() - scissors.y());
}
buffer.drawArrays(result.startVertex(), result.vertexCount());
if(pop) buffer.popScissors();
}
buffer.end();
}
public void beginFrame() {
builder.reset();
draws.clear();
layers.clear();
windowLayer = 1;
currentComponent = 0;
lastDrawer = null;
currentPipeline = null;
currentTexture = null;
clippingManager.clear();
matrix.popRoot();
matrix.setIdentity();
} }
@Override @Override
@@ -46,11 +98,13 @@ public class UIRenderer implements IUIRenderer {
@Override @Override
public void pushClip(IGuiBox box) { public void pushClip(IGuiBox box) {
flushDraw();
clippingManager.push(Math.round(box.getMinX()), Math.round(box.getMinY()), Math.round(box.getWidth()), Math.round(box.getHeight())); clippingManager.push(Math.round(box.getMinX()), Math.round(box.getMinY()), Math.round(box.getWidth()), Math.round(box.getHeight()));
} }
@Override @Override
public void popClip() { public void popClip() {
flushDraw();
clippingManager.pop(); clippingManager.pop();
} }
@@ -131,20 +185,6 @@ public class UIRenderer implements IUIRenderer {
} }
} }
/**
* Idea:
* public class DrawObject {
* int windowLayer;
* int componentLayer;
* ShaderPipeline pipeline;
* Int2ObjectMap<SampledTexture> textures;
* vec4 scissors;
* byte[] vertexData;
* }
*
* We pregenerate the draw call
*/
@Override @Override
public void drawLine(float startX, float startY, float endX, float endY, float lineWidth, int color) { public void drawLine(float startX, float startY, float endX, float endY, float lineWidth, int color) {
ensureType(UIConstants.GUI, null); ensureType(UIConstants.GUI, null);
@@ -225,8 +265,9 @@ public class UIRenderer implements IUIRenderer {
@Override @Override
public void drawCustom(UIDrawer drawer) { public void drawCustom(UIDrawer drawer) {
//TDOO ensure the draw type isn't present ensureType(drawer.pipeline(), drawer.texture());
drawer.buildVertecies(builder, matrix); drawer.buildVertecies(builder, matrix);
if(drawer.requiresSpecialArgs()) lastDrawer = drawer;
} }
protected void drawQuad(float minX, float minY, float maxX, float maxY, float xOff, float yOff, int color) { protected void drawQuad(float minX, float minY, float maxX, float maxY, float xOff, float yOff, int color) {
@@ -243,18 +284,25 @@ public class UIRenderer implements IUIRenderer {
return builder; return builder;
} }
protected void ensureType(ShaderPipeline pipeline, SampledTexture texture) { protected void ensureType(ShaderPipeline pipeline, @Nullable SampledTexture texture) {
if(currentPipeline == pipeline && currentTexture == texture) return; currentComponent++;
if(builder.hasVertecies()) { if(currentPipeline == pipeline && currentTexture == texture && lastDrawer == null) return;
draws.add(new UIDraw(currentPipeline, currentTexture, builder.finish())); flushDraw();
}
currentPipeline = pipeline; currentPipeline = pipeline;
currentTexture = texture; currentTexture = texture;
lastDrawer = null;
builder.start(pipeline.rasterizer().mode(), pipeline.attributes().get(0).layout());
} }
protected record UIDraw(ShaderPipeline pipeline, SampledTexture texture, BufferResult result) {} private void flushDraw() {
if(builder.hasVertecies()) {
draws.add(new UIDraw(currentPipeline, currentTexture, builder.finish(), clippingManager.top(), lastDrawer, layers.isEmpty() ? 0 : layers.top().currentLayer(), currentComponent));
}
}
private class WindowLayer { protected record UIDraw(ShaderPipeline pipeline, @Nullable SampledTexture texture, BufferResult result, Vec4i scissors, @Nullable UIDrawer drawer, int currentLayer, int componentIndex) {}
class WindowLayer {
final int layer; final int layer;
IntStack stack = new IntArrayList(); IntStack stack = new IntArrayList();
int currentLayer = 1; int currentLayer = 1;
@@ -271,7 +319,7 @@ public class UIRenderer implements IUIRenderer {
} }
public int currentLayer() { public int currentLayer() {
return BitUtil.toInt(layer, stack.isEmpty() ? 0 : stack.top()); return BitUtil.toInt(stack.isEmpty() ? 0 : stack.top(), layer);
} }
} }
} }
@@ -72,6 +72,12 @@ public class MathUtils {
return Math.signum(value); return Math.signum(value);
} }
public static int roundAlign(int value, int alignment) {
if(alignment == 0) return value;
int r = value % alignment;
return r == 0 ? value : value + (alignment - r);
}
public static int sub(int key, int value) { public static int sub(int key, int value) {
return key - value; return key - value;
} }