diff --git a/src/graphics/java/speiger/src/coreengine/platform/graphics/api/core/GraphicsCommandBuffer.java b/src/graphics/java/speiger/src/coreengine/platform/graphics/api/core/GraphicsCommandBuffer.java index cb3aebf..9ed1a96 100644 --- a/src/graphics/java/speiger/src/coreengine/platform/graphics/api/core/GraphicsCommandBuffer.java +++ b/src/graphics/java/speiger/src/coreengine/platform/graphics/api/core/GraphicsCommandBuffer.java @@ -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); diff --git a/src/graphics/java/speiger/src/coreengine/platform/graphics/api/core/GraphicsDevice.java b/src/graphics/java/speiger/src/coreengine/platform/graphics/api/core/GraphicsDevice.java index 993e2ab..2c92c42 100644 --- a/src/graphics/java/speiger/src/coreengine/platform/graphics/api/core/GraphicsDevice.java +++ b/src/graphics/java/speiger/src/coreengine/platform/graphics/api/core/GraphicsDevice.java @@ -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 graphics, List compute); } diff --git a/src/graphics/java/speiger/src/coreengine/platform/graphics/api/mesh/DynamicMesh.java b/src/graphics/java/speiger/src/coreengine/platform/graphics/api/mesh/DynamicMesh.java new file mode 100644 index 0000000..242f7a4 --- /dev/null +++ b/src/graphics/java/speiger/src/coreengine/platform/graphics/api/mesh/DynamicMesh.java @@ -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 buffers; + @Nullable + protected VertexBuffer indeciesBuffer; + @Nullable + protected IndeciesType indeciesType; + + public DynamicMesh(Int2ObjectMap 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 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 buffers() { + return allocateState.unmodifiable(); + } + + public BufferState getIndeciesBuffer() { + return allocateState.getOrDefault(-1, BufferState.STATIC_DRAW); + } + } +} diff --git a/src/graphics/java/speiger/src/coreengine/platform/graphics/api/mesh/Mesh.java b/src/graphics/java/speiger/src/coreengine/platform/graphics/api/mesh/Mesh.java index b4761d0..af2c276 100644 --- a/src/graphics/java/speiger/src/coreengine/platform/graphics/api/mesh/Mesh.java +++ b/src/graphics/java/speiger/src/coreengine/platform/graphics/api/mesh/Mesh.java @@ -16,25 +16,30 @@ import speiger.src.coreengine.platform.graphics.api.vertex.VertexLayout; public abstract class Mesh implements GraphicsResource { protected final Int2ObjectMap layouts; protected Int2ObjectMap buffers; + @Nullable protected VertexBuffer indeciesBuffer; + @Nullable protected IndeciesType indeciesType; public Mesh(Int2ObjectMap layouts, Int2ObjectMap 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 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 allocateState = new Int2ObjectOpenHashMap().setDefaultReturnValue(BufferState.STATIC_READ); + Int2ObjectMap allocateState = new Int2ObjectOpenHashMap().setDefaultReturnValue(BufferState.STATIC_DRAW); Int2ObjectMap layouts = Int2ObjectMap.builder().linkedMap(); IndeciesType type = IndeciesType.INT; OptionalInt preallocated = OptionalInt.empty(); diff --git a/src/graphics/java/speiger/src/coreengine/platform/graphics/api/vertex/builder/IVertexBuffer.java b/src/graphics/java/speiger/src/coreengine/platform/graphics/api/vertex/builder/IVertexBuffer.java index 06018a8..63f743c 100644 --- a/src/graphics/java/speiger/src/coreengine/platform/graphics/api/vertex/builder/IVertexBuffer.java +++ b/src/graphics/java/speiger/src/coreengine/platform/graphics/api/vertex/builder/IVertexBuffer.java @@ -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; } + } } diff --git a/src/graphics/java/speiger/src/coreengine/platform/graphics/api/vertex/builder/VertexBuilder.java b/src/graphics/java/speiger/src/coreengine/platform/graphics/api/vertex/builder/VertexBuilder.java index dcbe372..d3085d2 100644 --- a/src/graphics/java/speiger/src/coreengine/platform/graphics/api/vertex/builder/VertexBuilder.java +++ b/src/graphics/java/speiger/src/coreengine/platform/graphics/api/vertex/builder/VertexBuilder.java @@ -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; diff --git a/src/graphics/java/speiger/src/coreengine/platform/graphics/opengl/core/GLGraphicsDevice.java b/src/graphics/java/speiger/src/coreengine/platform/graphics/opengl/core/GLGraphicsDevice.java index 2fc548c..4f1910b 100644 --- a/src/graphics/java/speiger/src/coreengine/platform/graphics/opengl/core/GLGraphicsDevice.java +++ b/src/graphics/java/speiger/src/coreengine/platform/graphics/opengl/core/GLGraphicsDevice.java @@ -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 IMPORTS = Object2ObjectMap.builder().map().synchronize(); Object2IntMap shaderCache = new Object2IntOpenHashMap<>(); Object2ObjectMap 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 buffers = new Int2ObjectLinkedOpenHashMap<>(); + for(Int2ObjectMap.Entry 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); diff --git a/src/graphics/java/speiger/src/coreengine/platform/graphics/opengl/core/GLImmidateCommandBuffer.java b/src/graphics/java/speiger/src/coreengine/platform/graphics/opengl/core/GLImmidateCommandBuffer.java index 0f92b03..4262cbf 100644 --- a/src/graphics/java/speiger/src/coreengine/platform/graphics/opengl/core/GLImmidateCommandBuffer.java +++ b/src/graphics/java/speiger/src/coreengine/platform/graphics/opengl/core/GLImmidateCommandBuffer.java @@ -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); diff --git a/src/graphics/java/speiger/src/coreengine/platform/graphics/opengl/core/GLRecordingCommandBuffer.java b/src/graphics/java/speiger/src/coreengine/platform/graphics/opengl/core/GLRecordingCommandBuffer.java index 6747325..914fa6b 100644 --- a/src/graphics/java/speiger/src/coreengine/platform/graphics/opengl/core/GLRecordingCommandBuffer.java +++ b/src/graphics/java/speiger/src/coreengine/platform/graphics/opengl/core/GLRecordingCommandBuffer.java @@ -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() { diff --git a/src/graphics/java/speiger/src/coreengine/platform/graphics/opengl/mesh/GLDynamicMesh.java b/src/graphics/java/speiger/src/coreengine/platform/graphics/opengl/mesh/GLDynamicMesh.java new file mode 100644 index 0000000..8df379e --- /dev/null +++ b/src/graphics/java/speiger/src/coreengine/platform/graphics/opengl/mesh/GLDynamicMesh.java @@ -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 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; + } + +} diff --git a/src/graphics/java/speiger/src/coreengine/platform/graphics/opengl/mesh/GLMesh.java b/src/graphics/java/speiger/src/coreengine/platform/graphics/opengl/mesh/GLMesh.java index 0f8d7a9..3034cff 100644 --- a/src/graphics/java/speiger/src/coreengine/platform/graphics/opengl/mesh/GLMesh.java +++ b/src/graphics/java/speiger/src/coreengine/platform/graphics/opengl/mesh/GLMesh.java @@ -86,7 +86,7 @@ public class GLMesh extends Mesh { } @Override - protected void closeWithBuffers() { + public void closeWithBuffers() { boolean wasManaged = managed; managed = true; close(); diff --git a/src/graphics/java/speiger/src/coreengine/platform/graphics/opengl/mesh/VertexArrayCache.java b/src/graphics/java/speiger/src/coreengine/platform/graphics/opengl/mesh/VertexArrayCache.java new file mode 100644 index 0000000..5fb3b65 --- /dev/null +++ b/src/graphics/java/speiger/src/coreengine/platform/graphics/opengl/mesh/VertexArrayCache.java @@ -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 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; + } + + + } +} diff --git a/src/gui/java/speiger/src/coreengine/ui/gui/renderer/UIDrawer.java b/src/gui/java/speiger/src/coreengine/ui/gui/renderer/UIDrawer.java index 3806f99..c65591f 100644 --- a/src/gui/java/speiger/src/coreengine/ui/gui/renderer/UIDrawer.java +++ b/src/gui/java/speiger/src/coreengine/ui/gui/renderer/UIDrawer.java @@ -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); } \ No newline at end of file diff --git a/src/gui/java/speiger/src/coreengine/ui/gui/renderer/UIRenderer.java b/src/gui/java/speiger/src/coreengine/ui/gui/renderer/UIRenderer.java index 20f8ded..5734f7d 100644 --- a/src/gui/java/speiger/src/coreengine/ui/gui/renderer/UIRenderer.java +++ b/src/gui/java/speiger/src/coreengine/ui/gui/renderer/UIRenderer.java @@ -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 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); } } } diff --git a/src/math/java/speiger/src/coreengine/math/MathUtils.java b/src/math/java/speiger/src/coreengine/math/MathUtils.java index 4c4c839..c15a87d 100644 --- a/src/math/java/speiger/src/coreengine/math/MathUtils.java +++ b/src/math/java/speiger/src/coreengine/math/MathUtils.java @@ -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; }