shadow map pipeline batching work
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit

This commit is contained in:
austin 2025-05-20 18:54:57 -04:00
parent 93aca059d3
commit 26355baec4
8 changed files with 352 additions and 6 deletions

View File

@ -1,3 +1,3 @@
#maven.buildNumber.plugin properties file
#Mon May 19 19:19:06 EDT 2025
buildNumber=627
#Tue May 20 18:51:57 EDT 2025
buildNumber=629

View File

@ -122,6 +122,11 @@ public class ImGuiEntityActorTab {
ImGui.unindent();
}
//print static draw status
if(ImGui.collapsingHeader("Static Draw Status")){
ImGui.textWrapped(actor.getStatisDrawStatus());
}
//print data macros
if(ImGui.collapsingHeader("Print Data")){
//print bone values

View File

@ -54,6 +54,7 @@ import electrosphere.renderer.pipelines.UIPipeline;
import electrosphere.renderer.pipelines.VolumeBufferPipeline;
import electrosphere.renderer.pipelines.debug.DebugContentPipeline;
import electrosphere.renderer.shader.VisualShader;
import electrosphere.renderer.target.DrawTargetEvaluator;
import electrosphere.renderer.texture.Texture;
/**
@ -543,6 +544,11 @@ public class RenderingEngine {
if(this.RENDER_FLAG_RENDER_SCREEN_FRAMEBUFFER_CONTENT){
this.updateFrustumBox();
}
//determine draw targets
if(this.RENDER_FLAG_RENDER_SCREEN_FRAMEBUFFER_CONTENT && shouldRunPipelines()){
DrawTargetEvaluator.evaluate();
}
//generate depth map
if(this.RENDER_FLAG_RENDER_SHADOW_MAP && shouldRunPipelines()){

View File

@ -419,7 +419,7 @@ public class Actor {
* Applies the animation masks in this actor to the provided model
* @param model The model
*/
void applyAnimationMasks(Model model){
private void applyAnimationMasks(Model model){
List<String> bonesUsed = new LinkedList<String>();
List<String> currentAnimationMask = new LinkedList<String>();
for(ActorAnimationMask mask : animationQueue){
@ -522,6 +522,63 @@ public class Actor {
}
Globals.profiler.endCpuSample();
}
/**
* <p>
* Checks if the call to draw this actor would be a standard call.
* </p>
* <p>
* IE, does it apply a mesh mask, animation, shader mark, etc? If so, it's not a static draw call
* </p>
* @return true if it is a static call, false otherwise
*/
public boolean isStaticDrawCall(){
return
this.animationQueue.size() == 0 &&
this.meshMask.getBlockedMeshes().size() == 0 &&
this.textureMap == null &&
this.shaderMasks.size() == 0 &&
this.textureOverride == null &&
this.uniformMap.isEmpty() &&
this.staticMorph == null
;
}
/**
* <p>
* Checks if the call to draw this actor would be a standard call.
* </p>
* <p>
* IE, does it apply a mesh mask, animation, shader mark, etc? If so, it's not a static draw call
* </p>
* @return true if it is a static call, false otherwise
*/
public String getStatisDrawStatus(){
String rVal = "";
rVal = rVal + this.isStaticDrawCall() + "\n";
rVal = rVal + (this.animationQueue.size() == 0) + "\n";
rVal = rVal + (this.meshMask.getBlockedMeshes().size() == 0) + "\n";
rVal = rVal + (this.textureMap == null) + "\n";
rVal = rVal + (this.shaderMasks.size() == 0) + "\n";
rVal = rVal + (this.textureOverride == null) + "\n";
rVal = rVal + this.uniformMap.isEmpty() + "\n";
rVal = rVal + (this.staticMorph == null) + "\n";
return rVal;
}
/**
* Frustum tests the actor at a given position
* @param renderPipelineState The render pipeline state
* @param position The position to test at
* @return true if it should draw, false otherwise
*/
public boolean frustumTest(RenderPipelineState renderPipelineState, Vector3d position){
Model model = Globals.assetManager.fetchModel(modelPath);
if(model != null && Actor.isWithinFrustumBox(renderPipelineState,model,position,frustumCull)){
return true;
}
return false;
}
public void drawUI(){
Model model = Globals.assetManager.fetchModel(modelPath);
@ -781,6 +838,25 @@ public class Actor {
return check;
}
/**
* Checks if a given model is within the render pipeline state's frustum box
* @param renderPipelineState The render pipeline state
* @param model The model
* @param position The position of the model
* @param frustumCull Controls whether the frustum cull should actually be executed or not
* @return true if it is within the box, false otherwise
*/
static boolean isWithinFrustumBox(RenderPipelineState renderPipelineState, Model model, Vector3d position, boolean frustumCull){
if(!frustumCull){
return true;
}
Globals.profiler.beginAggregateCpuSample("Actor.isWithinFrustumBox");
Sphered sphere = model.getBoundingSphere();
boolean check = renderPipelineState.getFrustumIntersection().testSphere((float)(sphere.x + position.x), (float)(sphere.y + position.y), (float)(sphere.z + position.z), (float)sphere.r);
Globals.profiler.endCpuSample();
return check;
}
/**
* Gets the scaling applied by the actor
* @return The scaling

View File

@ -14,7 +14,7 @@ public class ActorUniformMap {
/**
* Map of mesh -> uniforms to push to that mesh
*/
Map<String,List<UniformValue>> meshMap = new HashMap<String,List<UniformValue>>();
private Map<String,List<UniformValue>> meshMap = new HashMap<String,List<UniformValue>>();
/**
* Sets the value of a uniform for a given mesh
@ -50,6 +50,14 @@ public class ActorUniformMap {
return meshMap.keySet();
}
/**
* Checks if the uniform map is empty
* @return true if it is empty, false otherwise
*/
public boolean isEmpty(){
return this.meshMap.isEmpty();
}
/**
* A uniform value

View File

@ -1,5 +1,8 @@
package electrosphere.renderer.pipelines;
import java.util.LinkedList;
import java.util.List;
import org.joml.Matrix4d;
import org.joml.Vector3d;
import org.lwjgl.opengl.GL40;
@ -8,12 +11,14 @@ import electrosphere.client.entity.camera.CameraEntityUtils;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityTags;
import electrosphere.entity.EntityUtils;
import electrosphere.renderer.OpenGLState;
import electrosphere.renderer.RenderPipelineState;
import electrosphere.renderer.RenderingEngine;
import electrosphere.renderer.actor.Actor;
import electrosphere.renderer.model.Model;
import electrosphere.renderer.target.DrawTargetAccumulator;
import electrosphere.renderer.target.DrawTargetAccumulator.ModelAccumulatorData;
import electrosphere.util.math.SpatialMathUtils;
/**
@ -51,6 +56,16 @@ public class ShadowMapPipeline implements RenderPipeline {
*/
boolean updateFarPlane = true;
/**
* The draw target accumulator
*/
private DrawTargetAccumulator drawTargetAccumulator = new DrawTargetAccumulator();
/**
* The queue for non-static entities to draw
*/
private List<Entity> standardDrawCall = new LinkedList<Entity>();
@Override
public void render(OpenGLState openGLState, RenderPipelineState renderPipelineState) {
Globals.profiler.beginCpuSample("ShadowMapPipeline.render");
@ -105,7 +120,7 @@ public class ShadowMapPipeline implements RenderPipeline {
modelTransformMatrix = new Matrix4d();
Vector3d posVec = new Vector3d();
Vector3d scaleVec = new Vector3d();
for(Entity currentEntity : Globals.clientState.clientScene.getEntitiesWithTag(EntityTags.DRAW_CAST_SHADOW)){
for(Entity currentEntity : this.standardDrawCall){
Vector3d position = EntityUtils.getPosition(currentEntity);
if(
currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW)!=null
@ -125,6 +140,22 @@ public class ShadowMapPipeline implements RenderPipeline {
currentActor.draw(renderPipelineState,openGLState);
}
}
for(ModelAccumulatorData accumulator : this.drawTargetAccumulator.getCalls()){
Model model = Globals.assetManager.fetchModel(accumulator.getModelPath());
if(model != null){
int count = accumulator.getCount();
List<Matrix4d> transforms = accumulator.getTransforms();
List<Vector3d> positions = accumulator.getPositions();
model.setMeshMask(null);
for(int i = 0; i < count; i++){
Vector3d position = positions.get(i);
Matrix4d transform = transforms.get(i);
model.setWorldPos(position);
model.setModelMatrix(transform);
model.draw(renderPipelineState, openGLState);
}
}
}
@ -156,5 +187,21 @@ public class ShadowMapPipeline implements RenderPipeline {
public void setUpdateFarPlane(boolean updateFarPlane){
this.updateFarPlane = updateFarPlane;
}
/**
* Gets the draw target accumulator
* @return The draw target accumulator
*/
public DrawTargetAccumulator getDrawTargetAccumulator(){
return drawTargetAccumulator;
}
/**
* Gets the queue of standard entities to draw
* @return The queue of standard entites
*/
public List<Entity> getStandardEntityQueue(){
return standardDrawCall;
}
}

View File

@ -0,0 +1,140 @@
package electrosphere.renderer.target;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.joml.Matrix4d;
import org.joml.Vector3d;
/**
* Accumulates non-spatially identical draw calls to batch to gpu
*/
public class DrawTargetAccumulator {
/**
* Structure that maps model path -> accumulator data for that model
*/
private Map<String,ModelAccumulatorData> modelPathAccumulatorMap = new HashMap<String,ModelAccumulatorData>();
/**
* Adds a call to draw a given model
* @param modelPath The path to the model
* @param position The position to draw the model at
* @param transform The transform to apply to the model
*/
public void addCall(String modelPath, Vector3d position, Matrix4d transform){
if(modelPathAccumulatorMap.containsKey(modelPath)){
ModelAccumulatorData data = modelPathAccumulatorMap.get(modelPath);
data.addCall(position, transform);
} else {
ModelAccumulatorData data = new ModelAccumulatorData(modelPath);
data.addCall(position, transform);
modelPathAccumulatorMap.put(modelPath,data);
}
}
/**
* Gets the calls to make
* @return The calls to make
*/
public Collection<ModelAccumulatorData> getCalls(){
return this.modelPathAccumulatorMap.values();
}
/**
* Clears calls for all models
*/
public void clearCalls(){
for(ModelAccumulatorData data : modelPathAccumulatorMap.values()){
data.count = 0;
}
}
/**
* Accumulates data about draw calls to be made for a specific model
*/
public static class ModelAccumulatorData {
/**
* The list of transform matricies to draw
*/
private List<Matrix4d> transforms = new ArrayList<Matrix4d>();
/**
* The list of positions to draw
*/
private List<Vector3d> positions = new ArrayList<Vector3d>();
/**
* The path to the model to draw
*/
private String modelPath;
/**
* The count of this model to draw
*/
private int count = 0;
/**
* Constructor
* @param path The model path
*/
public ModelAccumulatorData(String path){
this.modelPath = path;
}
/**
* Adds a call
* @param position The position of the call
* @param transform The transform of the call
*/
public void addCall(Vector3d position, Matrix4d transform){
if(transforms.size() < count){
transforms.get(count).set(transform);
positions.get(count).set(position);
} else {
transforms.add(new Matrix4d(transform));
positions.add(new Vector3d(position));
}
this.count++;
}
/**
* Gets the model path to draw
* @return The model path to draw
*/
public String getModelPath(){
return modelPath;
}
/**
* Gets the count to draw
* @return The count to draw
*/
public int getCount(){
return count;
}
/**
* Gets the list of transforms
* @return The list of transforms
*/
public List<Matrix4d> getTransforms(){
return transforms;
}
/**
* Gets the list of positions
* @return The list of positions
*/
public List<Vector3d> getPositions(){
return positions;
}
}
}

View File

@ -0,0 +1,64 @@
package electrosphere.renderer.target;
import java.util.List;
import org.joml.Matrix4d;
import org.joml.Vector3d;
import electrosphere.client.entity.camera.CameraEntityUtils;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityTags;
import electrosphere.entity.EntityUtils;
import electrosphere.renderer.actor.Actor;
/**
* Evaluates the draw targets
*/
public class DrawTargetEvaluator {
/**
* Evaluates the draw targets
*/
public static void evaluate(){
DrawTargetAccumulator shadowAccumulator = Globals.renderingEngine.getShadowMapPipeline().getDrawTargetAccumulator();
List<Entity> shadowQueue = Globals.renderingEngine.getShadowMapPipeline().getStandardEntityQueue();
shadowAccumulator.clearCalls();
shadowQueue.clear();
//reused objects
Vector3d scaleVec = new Vector3d();
Vector3d posVec = new Vector3d();
Matrix4d modelTransformMatrix = new Matrix4d();
//calculate camera-modified vector3d
Vector3d cameraCenter = new Vector3d(CameraEntityUtils.getCameraCenter(Globals.clientState.playerCamera));
//iterate over all shadow cast entities
for(Entity currentEntity : Globals.clientState.clientScene.getEntitiesWithTag(EntityTags.DRAW_CAST_SHADOW)){
if(currentEntity.getData(EntityDataStrings.DATA_STRING_DRAW)!=null){
Vector3d position = EntityUtils.getPosition(currentEntity);
//fetch actor
Actor currentActor = EntityUtils.getActor(currentEntity);
if(currentActor.isStaticDrawCall()){
Vector3d cameraModifiedPosition = posVec.set(position).sub(cameraCenter);
//calculate and apply model transform
modelTransformMatrix = modelTransformMatrix.identity();
modelTransformMatrix.translate(cameraModifiedPosition);
modelTransformMatrix.rotate(EntityUtils.getRotation(currentEntity));
modelTransformMatrix.scale(scaleVec.set(EntityUtils.getScale(currentEntity)));
posVec.set(0,0,0);
if(currentActor.frustumTest(Globals.renderingEngine.getRenderPipelineState(), modelTransformMatrix.getTranslation(posVec))){
//queue for batching
shadowAccumulator.addCall(currentActor.getModelPath(), position, modelTransformMatrix);
}
} else {
//queue for not-batching
shadowQueue.add(currentEntity);
}
}
}
}
}