Skip to content

Commit d4cf1bb

Browse files
committed
trying to improve geometry batching and rendering
1 parent 947cfd5 commit d4cf1bb

5 files changed

Lines changed: 163 additions & 123 deletions

File tree

jme3-core/src/main/java/com/jme3/backend/SimpleVulkanEngine.java

Lines changed: 117 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.jme3.light.Light;
77
import com.jme3.light.PointLight;
88
import com.jme3.light.SpotLight;
9+
import com.jme3.material.Material;
910
import com.jme3.material.RenderState;
1011
import com.jme3.material.plugins.VulkanMaterialLoader;
1112
import com.jme3.math.ColorRGBA;
@@ -53,15 +54,14 @@
5354
import com.jme3.vulkan.material.NewMaterialDef;
5455
import com.jme3.vulkan.material.VulkanMaterial;
5556
import com.jme3.vulkan.material.shader.ShaderModule;
57+
import com.jme3.vulkan.material.technique.NewTechnique;
58+
import com.jme3.vulkan.material.technique.VulkanTechnique;
5659
import com.jme3.vulkan.material.uniforms.TextureUniform;
5760
import com.jme3.vulkan.material.uniforms.Uniform;
5861
import com.jme3.vulkan.material.uniforms.StructUniform;
5962
import com.jme3.vulkan.memory.MemoryProp;
6063
import com.jme3.vulkan.memory.MemorySize;
61-
import com.jme3.vulkan.mesh.AdaptiveMesh;
62-
import com.jme3.vulkan.mesh.AdaptiveVertexBinding;
63-
import com.jme3.vulkan.mesh.InputRate;
64-
import com.jme3.vulkan.mesh.MeshLayout;
64+
import com.jme3.vulkan.mesh.*;
6565
import com.jme3.vulkan.mesh.attribute.*;
6666
import com.jme3.vulkan.pass.Attachment;
6767
import com.jme3.vulkan.pass.RenderPass;
@@ -71,6 +71,7 @@
7171
import com.jme3.vulkan.pipeline.framebuffer.FrameBuffer;
7272
import com.jme3.vulkan.pipeline.framebuffer.OutputFrameBuffer;
7373
import com.jme3.vulkan.pipeline.graphics.GraphicsPipeline;
74+
import com.jme3.vulkan.render.AbstractBatchElement;
7475
import com.jme3.vulkan.render.GeometryBatch;
7576
import com.jme3.vulkan.render.RenderEngine;
7677
import com.jme3.vulkan.render.VulkanGeometryBatch;
@@ -172,7 +173,11 @@ public static class Transforms implements Struct {
172173
private OutputFrameBuffer outFrameBuffer;
173174
private final BufferGenerator<VulkanBuffer> bufferGen = new BufferGeneratorImpl();
174175

175-
private VulkanGeometryBatch opaque, sky, transparent, gui, translucent;
176+
private final GeometryBatch<Element> opaque = new GeometryBatch<>(new OpaqueComparator());
177+
private final GeometryBatch<Element> sky = new GeometryBatch<>(new OpaqueComparator());
178+
private final GeometryBatch<Element> transparent = new GeometryBatch<>(new TransparentComparator());
179+
private final GeometryBatch<Element> gui = new GeometryBatch<>(new GuiComparator());
180+
private final GeometryBatch<Element> translucent = new GeometryBatch<>(new TransparentComparator());
176181

177182
private final Cache<Pipeline> pipelineCache = new Cache<>();
178183
private final Cache<PipelineLayout> pipelineLayoutCache = new Cache<>();
@@ -270,13 +275,7 @@ private void initialize() {
270275
}));
271276
});
272277

273-
opaque = new EngineGeometryBatch(new OpaqueComparator(), descriptorPool);
274-
sky = new EngineGeometryBatch(new OpaqueComparator(), descriptorPool);
275-
transparent = new EngineGeometryBatch(new TransparentComparator(), descriptorPool);
276-
gui = new EngineGeometryBatch(new GuiComparator(), descriptorPool);
277-
translucent = new EngineGeometryBatch(new TransparentComparator(), descriptorPool);
278278
outFrameBuffer = new OutputFrameBuffer(swapchain, renderPass);
279-
280279
stream = new BufferStream(device, 500);
281280
for (int i = 0; i < framesInFlight.length; i++) {
282281
framesInFlight[i] = new Frame();
@@ -417,40 +416,38 @@ private void render(ViewPort vp) {
417416
for (Spatial scene : vp.getScenes()) for (Spatial.GraphIterator it = scene.iterator(cull, bucket); it.hasNext();) {
418417
Spatial child = it.next();
419418
child.runControlRender(SimpleVulkanEngine.this, vp);
420-
if (cull.peek() != Camera.FrustumIntersect.Outside) {
421-
if (child instanceof Geometry) {
422-
Geometry g = (Geometry) child;
423-
if (vp.getGeometryFilter() == null || vp.getGeometryFilter().test(g)) {
424-
GeometryBatch<?> batch = getBatchByQueueBucket(bucket.peek());
425-
if (batch != null) {
426-
batch.add(g);
427-
}
419+
if (cull.peek() == Camera.FrustumIntersect.Outside) {
420+
it.skipChildren();
421+
} else if (child instanceof Geometry) {
422+
Geometry g = (Geometry)child;
423+
if (vp.getGeometryFilter() == null || vp.getGeometryFilter().test(g)) {
424+
GeometryBatch<?> batch = getBatchByQueueBucket(bucket.peek());
425+
if (batch != null) {
426+
batch.add(new Element(g, null, ));
428427
}
429428
}
430-
} else {
431-
it.skipChildren();
432429
}
433430
}
434431

435-
CommandSetting<ViewPortArea> vpArea = CommandSetting.viewPort();
436-
CommandSetting<ScissorArea> scissor = CommandSetting.scissor();
432+
CommandSetting<ViewPortArea> vpArea = graphics.addSetting(CommandSetting.viewPort());
433+
CommandSetting<ScissorArea> scissor = graphics.addSetting(CommandSetting.scissor());
437434

438435
vpArea.push(vp.getArea());
439436
scissor.push(vp.getArea().toScissor(null));
440437
{
441-
opaque.render(graphics, vpArea, scissor);
438+
renderBatch(opaque);
442439
vpArea.push(vp.getArea().clone().toMaxDepth());
443440
{
444-
sky.render(graphics, vpArea, scissor);
441+
renderBatch(sky);
445442
}
446443
vpArea.pop();
447-
transparent.render(graphics, vpArea, scissor);
444+
renderBatch(transparent);
448445
vpArea.push(vp.getArea().clone().toMinDepth());
449446
{
450-
gui.render(graphics, vpArea, scissor);
447+
renderBatch(gui);
451448
}
452449
vpArea.pop();
453-
translucent.render(graphics, vpArea, scissor);
450+
renderBatch(translucent);
454451
}
455452
vpArea.pop();
456453
scissor.pop();
@@ -464,67 +461,22 @@ private void render(ViewPort vp) {
464461

465462
}
466463

467-
private GeometryBatch<?> getBatchByQueueBucket(RenderQueue.Bucket bucket) {
468-
switch (bucket) {
469-
case Opaque: return opaque;
470-
case Sky: return sky;
471-
case Transparent: return transparent;
472-
case Gui: return gui;
473-
case Translucent: return translucent;
474-
default: return null;
475-
}
476-
}
477-
478-
}
479-
480-
private class EngineGeometryBatch extends VulkanGeometryBatch {
481-
482-
private final RenderState tempState = new RenderState();
483-
484-
public EngineGeometryBatch(Comparator<? super Element> comparator, DescriptorPool pool) {
485-
super(comparator, pool);
486-
}
487-
488-
@Override
489-
protected VertexPipeline createPipeline(Element e) {
490-
return GraphicsPipeline.build(device, p -> {
491-
p.setCache(pipelineCache);
492-
p.setLayoutCache(pipelineLayoutCache);
493-
p.setShaderCache(shaderCache);
494-
p.setSetLayoutCache(descSetLayoutCache);
495-
p.setSubpass(renderPass.getSubpass(0));
496-
p.setDynamic(DynamicState.ViewPort, true);
497-
p.setDynamic(DynamicState.Scissor, true);
498-
p.setDepthClamp(true);
499-
p.applyGeometry(app.getAssetManager(), e.getMesh(), e.getMaterial(), e.getTechnique());
500-
p.applyRenderState(tempState.integrateGeometryStates(e.getGeometry(), forcedRenderState,
501-
e.getMaterial().getAdditionalRenderState(), e.getTechnique().getRenderState()));
502-
});
503-
}
504-
505-
@Override
506-
public void render(CommandBuffer cmd, Runnable onRender) {
507-
if (onRender != null && !queue.isEmpty()) {
508-
onRender.run();
464+
private void renderBatch(GeometryBatch<Element> batch) {
465+
if (batch.isEmpty()) {
466+
return;
509467
}
510-
transforms.viewProjectionMatrix.set(camera.getViewProjectionMatrix());
468+
graphics.applySettings();
511469
VertexPipeline currentPipeline = null;
512470
VulkanMaterial currentMaterial = null;
513471
TempVars vars = TempVars.get();
514-
for (Element e : queue) {
515-
516-
// bind pipeline if necessary
472+
for (Element e : batch) {
517473
if (e.getPipeline() != currentPipeline) {
518-
(currentPipeline = e.getPipeline()).bind(cmd);
474+
(currentPipeline = e.getPipeline()).bind(graphics);
519475
currentMaterial = null;
520476
}
521-
522-
// bind material if necessary
523477
if (e.getMaterial() != currentMaterial) {
524-
(currentMaterial = e.getMaterial()).bind(cmd, currentPipeline, descriptorPool);
478+
(currentMaterial = e.getMaterial()).bind(graphics, currentPipeline, descriptorPool);
525479
}
526-
527-
// find light indices
528480
lighting.indices.clear();
529481
int lightIndex = 0;
530482
for (LightData l : lighting.lights) {
@@ -533,17 +485,26 @@ public void render(CommandBuffer cmd, Runnable onRender) {
533485
}
534486
lightIndex++;
535487
}
536-
537488
transforms.viewProjectionMatrix.mult(e.getGeometry().getWorldMatrix(), transforms.worldViewProjectionMatrix);
538489
currentMaterial.set("Lighting", lighting);
539490
currentMaterial.set("Transforms", transforms);
540-
stream.stream(cmd);
541-
542-
// render
543-
e.getMesh().render(cmd, currentPipeline);
491+
stream.stream(graphics); // this is a problem
492+
e.getMesh().render(graphics, e.getPipeline());
544493
}
545494
vars.release();
546495
}
496+
497+
private GeometryBatch<?> getBatchByQueueBucket(RenderQueue.Bucket bucket) {
498+
switch (bucket) {
499+
case Opaque: return opaque;
500+
case Sky: return sky;
501+
case Transparent: return transparent;
502+
case Gui: return gui;
503+
case Translucent: return translucent;
504+
default: return null;
505+
}
506+
}
507+
547508
}
548509

549510
private class SwapchainUpdater implements Consumer<Swapchain> {
@@ -584,4 +545,75 @@ public VulkanBuffer createBuffer(MemorySize size, Flag<BufferUsage> bufUsage, Gl
584545

585546
}
586547

548+
private class Element extends AbstractBatchElement {
549+
550+
private final VulkanTechnique technique;
551+
private final VulkanMaterial material;
552+
private final VulkanMesh mesh;
553+
private final VertexPipeline pipeline;
554+
555+
private Element(Geometry geometry, RenderState forcedRenderState,
556+
Material forcedMaterial, String forcedTechnique, Mesh forcedMesh) {
557+
super(geometry);
558+
Material mat = forcedMaterial != null ? forcedMaterial : geometry.getMaterial();
559+
if (!(mat instanceof NewMaterial)) {
560+
throw new ClassCastException("Cannot render " + mat.getClass() + " in a Vulkan context.");
561+
}
562+
this.material = (VulkanMaterial)mat;
563+
this.technique = material.getTechnique(forcedTechnique != null ? forcedTechnique : "main");
564+
Mesh mesh = forcedMesh != null ? forcedMesh : geometry.getMesh();
565+
if (!(mesh instanceof VulkanMesh)) {
566+
throw new ClassCastException("Cannot render " + mesh.getClass() + " in a Vulkan context.");
567+
}
568+
this.mesh = (VulkanMesh)mesh;
569+
this.pipeline = GraphicsPipeline.build(device, p -> {
570+
p.setCache(pipelineCache);
571+
p.setLayoutCache(pipelineLayoutCache);
572+
p.setShaderCache(shaderCache);
573+
p.setSetLayoutCache(descSetLayoutCache);
574+
p.setSubpass(renderPass.getSubpass(0));
575+
p.setDynamic(DynamicState.ViewPort, true);
576+
p.setDynamic(DynamicState.Scissor, true);
577+
p.setDepthClamp(true);
578+
p.applyGeometry(app.getAssetManager(), this.mesh, this.material, this.technique);
579+
p.applyRenderState(new RenderState().integrateGeometryStates(geometry, forcedRenderState,
580+
this.material.getAdditionalRenderState(), this.technique.getRenderState()));
581+
});
582+
}
583+
584+
@Override
585+
public Camera getCamera() {
586+
return VulkanGeometryBatch.this.getCamera();
587+
}
588+
589+
@Override
590+
public VulkanMaterial getMaterial() {
591+
return material;
592+
}
593+
594+
@Override
595+
public VulkanMesh getMesh() {
596+
return mesh;
597+
}
598+
599+
@Override
600+
public long getPipelineSortId() {
601+
return pipeline.getSortId();
602+
}
603+
604+
@Override
605+
public long getMaterialSortId() {
606+
return 0;
607+
}
608+
609+
public VulkanTechnique getTechnique() {
610+
return technique;
611+
}
612+
613+
public VertexPipeline getPipeline() {
614+
return pipeline;
615+
}
616+
617+
}
618+
587619
}

jme3-core/src/main/java/com/jme3/util/struct/StructLayout.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,9 +322,25 @@ public void updateBuffer(Struct src, BufferMapping dst, boolean force) {
322322
}
323323
}
324324

325+
public void updateBuffer(Struct src, BufferMapping dst, String... fieldNames) {
326+
updateBuffer(src, dst, false, fieldNames);
327+
}
328+
329+
public void updateBuffer(Struct src, BufferMapping dst, boolean force, String... fieldNames) {
330+
try {
331+
int p = dst.getBytes().position();
332+
getFieldDescription(src.getClass()).write(this, dst, src, force, fieldNames);
333+
dst.getBytes().position(p);
334+
} catch (IllegalAccessException e) {
335+
throw new RuntimeException("Failed to update buffer.", e);
336+
}
337+
}
338+
325339
public void updateStruct(ByteBuffer src, Struct dst) {
326340
try {
341+
int p = src.position();
327342
getFieldDescription(dst.getClass()).read(this, src, dst);
343+
src.position(p);
328344
} catch (IllegalAccessException e) {
329345
throw new RuntimeException("Failed to update struct.", e);
330346
}
@@ -388,6 +404,10 @@ public interface FieldDesc <T> {
388404

389405
T read(StructLayout layout, ByteBuffer buffer, T value) throws IllegalAccessException;
390406

407+
default void write(StructLayout layout, BufferMapping mapping, T value, boolean force, String... fieldNames) throws IllegalAccessException {
408+
write(layout, mapping, value, force);
409+
}
410+
391411
}
392412

393413
public static class StructDesc implements FieldDesc<Struct> {
@@ -417,6 +437,18 @@ public void write(StructLayout layout, BufferMapping mapping, Struct value, bool
417437
alignBuffer(mapping.getBytes(), info.getAlignment());
418438
}
419439

440+
@Override
441+
public void write(StructLayout layout, BufferMapping mapping, Struct value, boolean force, String... fieldNames) throws IllegalAccessException {
442+
StructInfo info = layout.getStructInfo(value.getClass());
443+
alignBuffer(mapping.getBytes(), info.getAlignment());
444+
for (Field f : info.getFields()) {
445+
if (Arrays.stream(fieldNames).anyMatch(n -> n.equals(f.getName()))) {
446+
layout.getFieldDescription(f.getType()).write(layout, mapping, f.get(value), force);
447+
}
448+
}
449+
alignBuffer(mapping.getBytes(), info.getAlignment());
450+
}
451+
420452
@Override
421453
public Struct read(StructLayout layout, ByteBuffer buffer, Struct value) throws IllegalAccessException {
422454
StructInfo info = layout.getStructInfo(value.getClass());

jme3-core/src/main/java/com/jme3/vulkan/commands/CommandBuffer.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,15 @@ public void reset() {
200200
completionListeners.clear();
201201
}
202202

203+
public <T extends CommandSetting> T addSetting(T setting) {
204+
// add setting
205+
return setting;
206+
}
207+
208+
public void applySettings() {
209+
// apply settings
210+
}
211+
203212
/**
204213
* Registers a semaphore to be signaled when the command buffer finishes
205214
* execution. All registered signal semaphores are removed when this

0 commit comments

Comments
 (0)