Progress on UI Renderer, getting to a good level :)
This commit is contained in:
+4
@@ -1,6 +1,7 @@
|
||||
package speiger.src.coreengine.platform.graphics.api.core;
|
||||
|
||||
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.shader.ShaderPipeline;
|
||||
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 pipeline(ShaderPipeline pipeline);
|
||||
|
||||
GraphicsCommandBuffer mesh(Mesh mesh);
|
||||
GraphicsCommandBuffer mesh(DynamicMesh mesh);
|
||||
|
||||
GraphicsCommandBuffer texture(int binding, SampledTexture texture);
|
||||
GraphicsCommandBuffer texture(int binding, Texture texture, AccessType access);
|
||||
|
||||
|
||||
+2
@@ -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.BufferType;
|
||||
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.sampler.Sampler;
|
||||
import speiger.src.coreengine.platform.graphics.api.sampler.SamplerSettings;
|
||||
@@ -28,6 +29,7 @@ public interface GraphicsDevice {
|
||||
Texture createTexture(TextureSettings data, int width, int height);
|
||||
Sampler createSampler(SamplerSettings settings);
|
||||
Mesh createMesh(Mesh.Builder builder);
|
||||
DynamicMesh createDynamicMesh(DynamicMesh.Builder builder);
|
||||
TimeQueryPool createQueryPool(int size);
|
||||
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 {
|
||||
protected final Int2ObjectMap<LayoutInfo> layouts;
|
||||
protected Int2ObjectMap<VertexBuffer> buffers;
|
||||
@Nullable
|
||||
protected VertexBuffer indeciesBuffer;
|
||||
@Nullable
|
||||
protected IndeciesType indeciesType;
|
||||
|
||||
public Mesh(Int2ObjectMap<LayoutInfo> layouts, Int2ObjectMap<VertexBuffer> buffers, @Nullable VertexBuffer indeciesBuffer, @Nullable IndeciesType type) {
|
||||
this.layouts = layouts;
|
||||
this.buffers = buffers;
|
||||
this.indeciesBuffer = indeciesBuffer;
|
||||
this.indeciesType = type;
|
||||
}
|
||||
|
||||
protected abstract void closeWithBuffers();
|
||||
public abstract void closeWithBuffers();
|
||||
|
||||
public Int2ObjectMap<LayoutInfo> layouts() {
|
||||
return layouts;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public VertexBuffer indeciesBuffer() {
|
||||
return indeciesBuffer;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public IndeciesType indeciesType() {
|
||||
return indeciesType;
|
||||
}
|
||||
@@ -43,13 +48,13 @@ public abstract class Mesh implements GraphicsResource {
|
||||
return buffers.get(binding);
|
||||
}
|
||||
|
||||
public Mesh indexBuffer(VertexBuffer indexBuffer, IndeciesType indeciesType) {
|
||||
public Mesh indexBuffer(@Nullable VertexBuffer indexBuffer, @Nullable IndeciesType indeciesType) {
|
||||
this.indeciesBuffer = indexBuffer;
|
||||
this.indeciesType = indeciesType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Mesh buffer(int binding, VertexBuffer buffer) {
|
||||
public Mesh buffer(int binding, @Nullable VertexBuffer buffer) {
|
||||
buffers.put(binding, buffer);
|
||||
return this;
|
||||
}
|
||||
@@ -59,7 +64,7 @@ public abstract class Mesh implements GraphicsResource {
|
||||
}
|
||||
|
||||
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();
|
||||
IndeciesType type = IndeciesType.INT;
|
||||
OptionalInt preallocated = OptionalInt.empty();
|
||||
|
||||
+3
-1
@@ -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+"]");
|
||||
}
|
||||
|
||||
public record BufferResult(DrawMode mode, int startVertex, int endVertex) {}
|
||||
public record BufferResult(DrawMode mode, int startVertex, int endVertex) {
|
||||
public int vertexCount() { return endVertex - startVertex; }
|
||||
}
|
||||
}
|
||||
|
||||
+22
-2
@@ -1,14 +1,17 @@
|
||||
package speiger.src.coreengine.platform.graphics.api.vertex.builder;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Objects;
|
||||
|
||||
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.vertex.VertexLayout;
|
||||
import speiger.src.coreengine.platform.graphics.api.vertex.VertexLayout.Element;
|
||||
|
||||
public class VertexBuilder implements IVertexBuffer, AutoCloseable {
|
||||
boolean autoAlign;
|
||||
boolean noFree = false;
|
||||
DrawMode mode;
|
||||
ByteBuffer buffer;
|
||||
@@ -29,6 +32,11 @@ public class VertexBuilder implements IVertexBuffer, AutoCloseable {
|
||||
this.buffer = buffer;
|
||||
}
|
||||
|
||||
public VertexBuilder withAutoAlign() {
|
||||
this.autoAlign = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public VertexBuilder withoutFree() {
|
||||
noFree = true;
|
||||
return this;
|
||||
@@ -36,8 +44,12 @@ public class VertexBuilder implements IVertexBuffer, AutoCloseable {
|
||||
|
||||
public VertexBuilder start(DrawMode mode, VertexLayout layout) {
|
||||
if(index != 0) throw new IllegalStateException("Can't change Layout mid Writing");
|
||||
this.mode = mode;
|
||||
this.layout = layout;
|
||||
if(autoAlign && 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;
|
||||
}
|
||||
|
||||
@@ -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 DrawMode mode() { return mode; }
|
||||
public VertexLayout getFormat() { return layout; }
|
||||
public long memoryPointer() { return MemoryUtil.memAddress(buffer); }
|
||||
public int totalStoredBytes() { return totalStoredBytes; }
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
@@ -70,6 +84,12 @@ public class VertexBuilder implements IVertexBuffer, AutoCloseable {
|
||||
|
||||
@Override
|
||||
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);
|
||||
lastVertecies = vertecies;
|
||||
return result;
|
||||
|
||||
+21
-1
@@ -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.GraphicsDevice;
|
||||
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.LayoutInfo;
|
||||
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.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.BufferMode;
|
||||
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.ShaderType;
|
||||
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.TextureType;
|
||||
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.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.VertexArrayCache;
|
||||
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.ShaderInstance;
|
||||
@@ -78,6 +81,7 @@ public class GLGraphicsDevice implements GraphicsDevice {
|
||||
static final Map<ID, String> IMPORTS = Object2ObjectMap.builder().<ID, String>map().synchronize();
|
||||
Object2IntMap<ID> shaderCache = new Object2IntOpenHashMap<>();
|
||||
Object2ObjectMap<ID, ShaderInstance> programCache = new Object2ObjectOpenHashMap<>();
|
||||
VertexArrayCache vertexCache = new VertexArrayCache();
|
||||
AssetManager manager;
|
||||
GLCapabilities capabilities;
|
||||
GLStates states = new GLStates();
|
||||
@@ -167,6 +171,22 @@ public class GLGraphicsDevice implements GraphicsDevice {
|
||||
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
|
||||
public TimeQueryPool createQueryPool(int size) {
|
||||
return new GLTimeQueryPool(size);
|
||||
|
||||
+21
@@ -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.states.BufferType;
|
||||
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.shader.ColorTarget;
|
||||
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.ScissorsManager;
|
||||
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.sampler.GLSampler;
|
||||
import speiger.src.coreengine.platform.graphics.opengl.shader.ShaderInstance;
|
||||
@@ -39,6 +41,7 @@ public class GLImmidateCommandBuffer implements GraphicsCommandBuffer {
|
||||
ShaderPipeline pipeline;
|
||||
ShaderInstance shader;
|
||||
GLMesh mesh;
|
||||
GLDynamicMesh dynamicMesh;
|
||||
|
||||
public GLImmidateCommandBuffer(GLGraphicsDevice device) {
|
||||
this.device = device;
|
||||
@@ -102,6 +105,7 @@ public class GLImmidateCommandBuffer implements GraphicsCommandBuffer {
|
||||
ColorTarget color = pipeline.colorTarget();
|
||||
color.blend().ifPresentOrElse(states.blend::enableSet, states.blend::disable);
|
||||
states.setColorMask(color.writeMask());
|
||||
if(dynamicMesh != null) bindDynamicMesh();
|
||||
recorded++;
|
||||
return this;
|
||||
}
|
||||
@@ -113,10 +117,27 @@ public class GLImmidateCommandBuffer implements GraphicsCommandBuffer {
|
||||
if(this.mesh == mesh) return this;
|
||||
this.mesh = (GLMesh)mesh;
|
||||
GL30.glBindVertexArray(this.mesh.id());
|
||||
this.dynamicMesh = null;
|
||||
recorded++;
|
||||
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
|
||||
public GraphicsCommandBuffer texture(int binding, SampledTexture texture) {
|
||||
Objects.requireNonNull(texture);
|
||||
|
||||
+24
-3
@@ -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.IndeciesType;
|
||||
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.shader.ColorTarget;
|
||||
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.NoOp;
|
||||
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.VertexArrayCache;
|
||||
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.texture.GLTexture;
|
||||
@@ -44,7 +47,7 @@ public class GLRecordingCommandBuffer implements GraphicsCommandBuffer {
|
||||
ShaderPipeline pipeline;
|
||||
ShaderInstance shader;
|
||||
GLMesh mesh;
|
||||
|
||||
GLDynamicMesh dynamicMesh;
|
||||
|
||||
public GLRecordingCommandBuffer(GLGraphicsDevice device) {
|
||||
this.device = device;
|
||||
@@ -95,7 +98,7 @@ public class GLRecordingCommandBuffer implements GraphicsCommandBuffer {
|
||||
//TODO implement logging
|
||||
return this;
|
||||
}
|
||||
tasks.add(new SetPipeline(pipeline, shader, device.states));
|
||||
tasks.add(new SetPipeline(pipeline, shader, dynamicMesh, device.states, device.vertexCache));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -106,6 +109,18 @@ public class GLRecordingCommandBuffer implements GraphicsCommandBuffer {
|
||||
if(this.mesh == mesh) return this;
|
||||
this.mesh = (GLMesh)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;
|
||||
}
|
||||
|
||||
@@ -238,7 +253,7 @@ public class GLRecordingCommandBuffer implements GraphicsCommandBuffer {
|
||||
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
|
||||
public void run() {
|
||||
states.shaders.bind(instance);
|
||||
@@ -255,6 +270,7 @@ public class GLRecordingCommandBuffer implements GraphicsCommandBuffer {
|
||||
ColorTarget color = pipeline.colorTarget();
|
||||
color.blend().ifPresentOrElse(states.blend::enableSet, states.blend::disable);
|
||||
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 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 {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
+39
@@ -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
|
||||
protected void closeWithBuffers() {
|
||||
public void closeWithBuffers() {
|
||||
boolean wasManaged = managed;
|
||||
managed = true;
|
||||
close();
|
||||
|
||||
+83
@@ -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;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import speiger.src.coreengine.math.vector.matrix.Matrix4f;
|
||||
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.utils.SampledTexture;
|
||||
import speiger.src.coreengine.platform.graphics.api.vertex.builder.IVertexBuilder;
|
||||
|
||||
public interface UIDrawer {
|
||||
ShaderPipeline pipeline();
|
||||
@Nullable
|
||||
SampledTexture texture();
|
||||
void buildVertecies(IVertexBuilder builder, Matrix4f matrix);
|
||||
boolean requiresSpecialArgs();
|
||||
void setupExtra(GraphicsCommandBuffer buffer);
|
||||
}
|
||||
@@ -1,20 +1,27 @@
|
||||
package speiger.src.coreengine.ui.gui.renderer;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import speiger.src.collections.ints.collections.IntStack;
|
||||
import speiger.src.collections.ints.lists.IntArrayList;
|
||||
import speiger.src.collections.objects.lists.ObjectArrayList;
|
||||
import speiger.src.collections.utils.Stack;
|
||||
import speiger.src.coreengine.math.MathUtils;
|
||||
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.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.mesh.DynamicMesh;
|
||||
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.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.IVertexBuilder;
|
||||
import speiger.src.coreengine.platform.graphics.api.vertex.builder.VertexBuilder;
|
||||
@@ -33,10 +40,55 @@ public class UIRenderer implements IUIRenderer {
|
||||
int currentComponent = 0;
|
||||
ShaderPipeline currentPipeline;
|
||||
SampledTexture currentTexture;
|
||||
UIDrawer lastDrawer;
|
||||
ScissorsManager clippingManager = new ScissorsManager(64, ScissorsManager.NoOp.INSTANCE);
|
||||
DynamicMesh mesh;
|
||||
|
||||
public UIRenderer(GraphicsDevice 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
|
||||
@@ -46,11 +98,13 @@ public class UIRenderer implements IUIRenderer {
|
||||
|
||||
@Override
|
||||
public void pushClip(IGuiBox box) {
|
||||
flushDraw();
|
||||
clippingManager.push(Math.round(box.getMinX()), Math.round(box.getMinY()), Math.round(box.getWidth()), Math.round(box.getHeight()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void popClip() {
|
||||
flushDraw();
|
||||
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
|
||||
public void drawLine(float startX, float startY, float endX, float endY, float lineWidth, int color) {
|
||||
ensureType(UIConstants.GUI, null);
|
||||
@@ -225,8 +265,9 @@ public class UIRenderer implements IUIRenderer {
|
||||
|
||||
@Override
|
||||
public void drawCustom(UIDrawer drawer) {
|
||||
//TDOO ensure the draw type isn't present
|
||||
ensureType(drawer.pipeline(), drawer.texture());
|
||||
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) {
|
||||
@@ -243,18 +284,25 @@ public class UIRenderer implements IUIRenderer {
|
||||
return builder;
|
||||
}
|
||||
|
||||
protected void ensureType(ShaderPipeline pipeline, SampledTexture texture) {
|
||||
if(currentPipeline == pipeline && currentTexture == texture) return;
|
||||
if(builder.hasVertecies()) {
|
||||
draws.add(new UIDraw(currentPipeline, currentTexture, builder.finish()));
|
||||
}
|
||||
protected void ensureType(ShaderPipeline pipeline, @Nullable SampledTexture texture) {
|
||||
currentComponent++;
|
||||
if(currentPipeline == pipeline && currentTexture == texture && lastDrawer == null) return;
|
||||
flushDraw();
|
||||
currentPipeline = pipeline;
|
||||
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;
|
||||
IntStack stack = new IntArrayList();
|
||||
int currentLayer = 1;
|
||||
@@ -271,7 +319,7 @@ public class UIRenderer implements IUIRenderer {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
return key - value;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user