shadow map pipeline batching work
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit
This commit is contained in:
parent
93aca059d3
commit
26355baec4
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()){
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user