diff --git a/README.md b/README.md index 73e58a641..2f576ff8b 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ The engine is used by several commercial game studios and computer-science cours - [3089 (on steam)](http://store.steampowered.com/app/263360/) - [3079 (on steam)](http://store.steampowered.com/app/259620/) - [Lightspeed Frontier](http://www.lightspeedfrontier.com/) + - [Skullstone](http://www.skullstonegame.com/) ## Getting started diff --git a/jme3-android/src/main/java/com/jme3/app/DefaultAndroidProfiler.java b/jme3-android/src/main/java/com/jme3/app/DefaultAndroidProfiler.java index 71a5dbd51..306887910 100644 --- a/jme3-android/src/main/java/com/jme3/app/DefaultAndroidProfiler.java +++ b/jme3-android/src/main/java/com/jme3/app/DefaultAndroidProfiler.java @@ -33,8 +33,8 @@ package com.jme3.app; import android.os.Build; -import com.jme3.profile.AppProfiler; -import com.jme3.profile.AppStep; +import com.jme3.profile.*; + import static com.jme3.profile.AppStep.BeginFrame; import static com.jme3.profile.AppStep.EndFrame; import static com.jme3.profile.AppStep.ProcessAudio; @@ -47,7 +47,6 @@ import static com.jme3.profile.AppStep.RenderPreviewViewPorts; import static com.jme3.profile.AppStep.SpatialUpdate; import static com.jme3.profile.AppStep.StateManagerRender; import static com.jme3.profile.AppStep.StateManagerUpdate; -import com.jme3.profile.VpStep; import static com.jme3.profile.VpStep.BeginRender; import static com.jme3.profile.VpStep.EndRender; import static com.jme3.profile.VpStep.FlushQueue; @@ -164,4 +163,10 @@ public class DefaultAndroidProfiler implements AppProfiler { } } } + + @Override + public void spStep(SpStep step, String... additionalInfo) { + + } + } diff --git a/jme3-android/src/main/java/com/jme3/app/state/VideoRecorderAppState.java b/jme3-android/src/main/java/com/jme3/app/state/VideoRecorderAppState.java index a388ad612..47d0e36ca 100644 --- a/jme3-android/src/main/java/com/jme3/app/state/VideoRecorderAppState.java +++ b/jme3-android/src/main/java/com/jme3/app/state/VideoRecorderAppState.java @@ -34,6 +34,7 @@ package com.jme3.app.state; import android.graphics.Bitmap; import com.jme3.app.Application; import com.jme3.post.SceneProcessor; +import com.jme3.profile.AppProfiler; import com.jme3.renderer.Camera; import com.jme3.renderer.RenderManager; import com.jme3.renderer.Renderer; @@ -226,6 +227,7 @@ public class VideoRecorderAppState extends AbstractAppState { private LinkedBlockingQueue usedItems = new LinkedBlockingQueue(); private MjpegFileWriter writer; private boolean fastMode = true; + private AppProfiler prof; public void addImage(Renderer renderer, FrameBuffer out) { if (freeItems == null) { @@ -313,6 +315,11 @@ public class VideoRecorderAppState extends AbstractAppState { } writer = null; } + + @Override + public void setProfiler(AppProfiler profiler) { + this.prof = profiler; + } } public static final class IsoTimer extends com.jme3.system.Timer { diff --git a/jme3-android/src/main/java/com/jme3/renderer/android/AndroidGL.java b/jme3-android/src/main/java/com/jme3/renderer/android/AndroidGL.java index 12e743810..3541eaa63 100644 --- a/jme3-android/src/main/java/com/jme3/renderer/android/AndroidGL.java +++ b/jme3-android/src/main/java/com/jme3/renderer/android/AndroidGL.java @@ -31,11 +31,10 @@ */ package com.jme3.renderer.android; -import android.opengl.GLES20; +import android.opengl.*; import com.jme3.renderer.RendererException; -import com.jme3.renderer.opengl.GL; -import com.jme3.renderer.opengl.GLExt; -import com.jme3.renderer.opengl.GLFbo; +import com.jme3.renderer.opengl.*; + import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.FloatBuffer; @@ -92,6 +91,11 @@ public class AndroidGL implements GL, GLExt, GLFbo { GLES20.glAttachShader(program, shader); } + @Override + public void glBeginQuery(int target, int query) { + GLES30.glBeginQuery(target, query); + } + public void glBindBuffer(int target, int buffer) { GLES20.glBindBuffer(target, buffer); } @@ -234,6 +238,11 @@ public class AndroidGL implements GL, GLExt, GLFbo { GLES20.glEnableVertexAttribArray(index); } + @Override + public void glEndQuery(int target) { + GLES30.glEndQuery(target); + } + public void glGenBuffers(IntBuffer buffers) { checkLimit(buffers); GLES20.glGenBuffers(buffers.limit(), buffers); @@ -244,6 +253,11 @@ public class AndroidGL implements GL, GLExt, GLFbo { GLES20.glGenTextures(textures.limit(), textures); } + @Override + public void glGenQueries(int num, IntBuffer buff) { + GLES30.glGenQueries(num, buff); + } + public int glGetAttribLocation(int program, String name) { return GLES20.glGetAttribLocation(program, name); } @@ -271,6 +285,21 @@ public class AndroidGL implements GL, GLExt, GLFbo { return GLES20.glGetProgramInfoLog(program); } + @Override + public long glGetQueryObjectui64(int query, int pname) { + IntBuffer buff = IntBuffer.allocate(1); + //FIXME This is wrong IMO should be glGetQueryObjectui64v with a LongBuffer but it seems the API doesn't provide it. + GLES30.glGetQueryObjectuiv(query, pname, buff); + return buff.get(0); + } + + @Override + public int glGetQueryObjectiv(int query, int pname) { + IntBuffer buff = IntBuffer.allocate(1); + GLES30.glGetQueryiv(query, pname, buff); + return buff.get(0); + } + public void glGetShader(int shader, int pname, IntBuffer params) { checkLimit(params); GLES20.glGetShaderiv(shader, pname, params); diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/control/CharacterControl.java b/jme3-bullet/src/common/java/com/jme3/bullet/control/CharacterControl.java index 7ef80778c..d12e5991a 100644 --- a/jme3-bullet/src/common/java/com/jme3/bullet/control/CharacterControl.java +++ b/jme3-bullet/src/common/java/com/jme3/bullet/control/CharacterControl.java @@ -121,6 +121,7 @@ public class CharacterControl extends PhysicsCharacter implements PhysicsControl control.setUpAxis(getUpAxis()); control.setApplyPhysicsLocal(isApplyPhysicsLocal()); control.spatial = this.spatial; + control.setEnabled(isEnabled()); return control; } @@ -207,11 +208,12 @@ public class CharacterControl extends PhysicsCharacter implements PhysicsControl added = false; } } else { - if (this.space == space) { - return; + if(this.space == space) return; + // if this object isn't enabled, it will be added when it will be enabled. + if (isEnabled()) { + space.addCollisionObject(this); + added = true; } - space.addCollisionObject(this); - added = true; } this.space = space; } diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/control/RigidBodyControl.java b/jme3-bullet/src/common/java/com/jme3/bullet/control/RigidBodyControl.java index 25a23cc7c..f2002be02 100644 --- a/jme3-bullet/src/common/java/com/jme3/bullet/control/RigidBodyControl.java +++ b/jme3-bullet/src/common/java/com/jme3/bullet/control/RigidBodyControl.java @@ -53,6 +53,7 @@ import com.jme3.scene.shape.Box; import com.jme3.scene.shape.Sphere; import com.jme3.util.clone.Cloner; import com.jme3.util.clone.JmeCloneable; + import java.io.IOException; /** @@ -143,6 +144,8 @@ public class RigidBodyControl extends PhysicsRigidBody implements PhysicsControl } control.setApplyPhysicsLocal(isApplyPhysicsLocal()); control.spatial = this.spatial; + control.setEnabled(isEnabled()); + return control; } @@ -273,9 +276,12 @@ public class RigidBodyControl extends PhysicsRigidBody implements PhysicsControl added = false; } } else { - if(this.space==space) return; - space.addCollisionObject(this); - added = true; + if (this.space == space) return; + // if this object isn't enabled, it will be added when it will be enabled. + if (isEnabled()) { + space.addCollisionObject(this); + added = true; + } } this.space = space; } diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/control/VehicleControl.java b/jme3-bullet/src/common/java/com/jme3/bullet/control/VehicleControl.java index 7ad36b3ae..5f6af8175 100644 --- a/jme3-bullet/src/common/java/com/jme3/bullet/control/VehicleControl.java +++ b/jme3-bullet/src/common/java/com/jme3/bullet/control/VehicleControl.java @@ -200,6 +200,7 @@ public class VehicleControl extends PhysicsVehicle implements PhysicsControl, Jm newWheel.setWheelSpatial(wheel.getWheelSpatial()); } control.setApplyPhysicsLocal(isApplyPhysicsLocal()); + control.setEnabled(isEnabled()); control.spatial = spatial; return control; @@ -268,9 +269,12 @@ public class VehicleControl extends PhysicsVehicle implements PhysicsControl, Jm added = false; } } else { - if(this.space==space) return; - space.addCollisionObject(this); - added = true; + if(this.space == space) return; + // if this object isn't enabled, it will be added when it will be enabled. + if (isEnabled()) { + space.addCollisionObject(this); + added = true; + } } this.space = space; } diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/MeshCollisionShape.java b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/MeshCollisionShape.java index 9f36d28a8..3c5dbbb49 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/MeshCollisionShape.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/MeshCollisionShape.java @@ -122,7 +122,7 @@ public class MeshCollisionShape extends CollisionShape { this.vertexStride = 12; this.triangleIndexStride = 12; this.memoryOptimized = memoryOptimized; - this.createShape(true); + this.createShape(null); } private void createCollisionMesh(Mesh mesh) { @@ -150,7 +150,7 @@ public class MeshCollisionShape extends CollisionShape { vertices.rewind(); vertices.clear(); - this.createShape(true); + this.createShape(null); } @Override @@ -191,25 +191,19 @@ public class MeshCollisionShape extends CollisionShape { this.vertexBase = BufferUtils.createByteBuffer(capsule.readByteArray(MeshCollisionShape.VERTEX_BASE, null)); byte[] nativeBvh = capsule.readByteArray(MeshCollisionShape.NATIVE_BVH, null); - if (nativeBvh == null) { - // Either using non memory optimized BVH or old J3O file - memoryOptimized = false; - createShape(true); - } else { - // Using memory optimized BVH, load from J3O, then assign it. - memoryOptimized = true; - createShape(false); - nativeBVHBuffer = setBVH(nativeBvh, this.objectId); - } + memoryOptimized=nativeBvh != null; + createShape(nativeBvh); } - private void createShape(boolean buildBvt) { + private void createShape(byte bvh[]) { + boolean buildBvh=bvh==null||bvh.length==0; this.meshId = NativeMeshUtil.createTriangleIndexVertexArray(this.triangleIndexBase, this.vertexBase, this.numTriangles, this.numVertices, this.vertexStride, this.triangleIndexStride); Logger.getLogger(this.getClass().getName()).log(Level.FINE, "Created Mesh {0}", Long.toHexString(this.meshId)); - this.objectId = createShape(memoryOptimized, buildBvt, this.meshId); + this.objectId = createShape(memoryOptimized, buildBvh, this.meshId); Logger.getLogger(this.getClass().getName()).log(Level.FINE, "Created Shape {0}", Long.toHexString(this.objectId)); + if(!buildBvh) nativeBVHBuffer = setBVH(bvh, this.objectId); this.setScale(this.scale); - this.setMargin(this.margin); + this.setMargin(this.margin); } /** diff --git a/jme3-core/src/main/java/com/jme3/app/BasicProfiler.java b/jme3-core/src/main/java/com/jme3/app/BasicProfiler.java index e36356040..d652e4488 100644 --- a/jme3-core/src/main/java/com/jme3/app/BasicProfiler.java +++ b/jme3-core/src/main/java/com/jme3/app/BasicProfiler.java @@ -32,9 +32,7 @@ package com.jme3.app; -import com.jme3.profile.AppProfiler; -import com.jme3.profile.AppStep; -import com.jme3.profile.VpStep; +import com.jme3.profile.*; import com.jme3.renderer.ViewPort; import com.jme3.renderer.queue.RenderQueue.Bucket; import com.jme3.scene.Mesh; @@ -191,7 +189,13 @@ public class BasicProfiler implements AppProfiler { @Override public void vpStep( VpStep step, ViewPort vp, Bucket bucket ) { - } + } + + @Override + public void spStep(SpStep step, String... additionalInfo) { + + } + } diff --git a/jme3-core/src/main/java/com/jme3/app/state/ScreenshotAppState.java b/jme3-core/src/main/java/com/jme3/app/state/ScreenshotAppState.java index c6ba5b124..ea4d5cd73 100644 --- a/jme3-core/src/main/java/com/jme3/app/state/ScreenshotAppState.java +++ b/jme3-core/src/main/java/com/jme3/app/state/ScreenshotAppState.java @@ -37,6 +37,7 @@ import com.jme3.input.KeyInput; import com.jme3.input.controls.ActionListener; import com.jme3.input.controls.KeyTrigger; import com.jme3.post.SceneProcessor; +import com.jme3.profile.AppProfiler; import com.jme3.renderer.Camera; import com.jme3.renderer.RenderManager; import com.jme3.renderer.Renderer; @@ -67,6 +68,7 @@ public class ScreenshotAppState extends AbstractAppState implements ActionListen private String shotName; private long shotIndex = 0; private int width, height; + private AppProfiler prof; /** * Using this constructor, the screenshot files will be written sequentially to the system @@ -256,7 +258,12 @@ public class ScreenshotAppState extends AbstractAppState implements ActionListen } } } - + + @Override + public void setProfiler(AppProfiler profiler) { + this.prof = profiler; + } + /** * Called by postFrame() once the screen has been captured to outBuf. */ diff --git a/jme3-core/src/main/java/com/jme3/cinematic/events/MotionEvent.java b/jme3-core/src/main/java/com/jme3/cinematic/events/MotionEvent.java index 77d920fe8..b94bd25d7 100644 --- a/jme3-core/src/main/java/com/jme3/cinematic/events/MotionEvent.java +++ b/jme3-core/src/main/java/com/jme3/cinematic/events/MotionEvent.java @@ -214,6 +214,7 @@ public class MotionEvent extends AbstractCinematicEvent implements Control, JmeC oc.write(rotation, "rotation", null); oc.write(directionType, "directionType", Direction.None); oc.write(path, "path", null); + oc.write(spatial, "spatial", null); } @Override @@ -225,6 +226,7 @@ public class MotionEvent extends AbstractCinematicEvent implements Control, JmeC rotation = (Quaternion) in.readSavable("rotation", null); directionType = in.readEnum("directionType", Direction.class, Direction.None); path = (MotionPath) in.readSavable("path", null); + spatial = (Spatial) in.readSavable("spatial", null); } /** diff --git a/jme3-core/src/main/java/com/jme3/effect/ParticleEmitter.java b/jme3-core/src/main/java/com/jme3/effect/ParticleEmitter.java index 1af2bf1d1..d5c0b9d75 100644 --- a/jme3-core/src/main/java/com/jme3/effect/ParticleEmitter.java +++ b/jme3-core/src/main/java/com/jme3/effect/ParticleEmitter.java @@ -1101,9 +1101,12 @@ public class ParticleEmitter extends Geometry { lastPos.set(getWorldTranslation()); - BoundingBox bbox = (BoundingBox) this.getMesh().getBound(); - bbox.setMinMax(min, max); - this.setBoundRefresh(); + //This check avoids a NaN bounds when all the particles are dead during the first update. + if (!min.equals(Vector3f.POSITIVE_INFINITY) && !max.equals(Vector3f.NEGATIVE_INFINITY)) { + BoundingBox bbox = (BoundingBox) this.getMesh().getBound(); + bbox.setMinMax(min, max); + this.setBoundRefresh(); + } vars.release(); } diff --git a/jme3-core/src/main/java/com/jme3/light/LightProbeBlendingProcessor.java b/jme3-core/src/main/java/com/jme3/light/LightProbeBlendingProcessor.java index 9e1ec344b..572fd5c18 100644 --- a/jme3-core/src/main/java/com/jme3/light/LightProbeBlendingProcessor.java +++ b/jme3-core/src/main/java/com/jme3/light/LightProbeBlendingProcessor.java @@ -33,6 +33,7 @@ package com.jme3.light; import com.jme3.bounding.BoundingSphere; import com.jme3.post.SceneProcessor; +import com.jme3.profile.AppProfiler; import com.jme3.renderer.RenderManager; import com.jme3.renderer.ViewPort; import com.jme3.renderer.queue.RenderQueue; @@ -56,6 +57,7 @@ public class LightProbeBlendingProcessor implements SceneProcessor { private RenderManager renderManager; private LightProbe probe = new LightProbe(); private Spatial poi; + private AppProfiler prof; public LightProbeBlendingProcessor(Spatial poi) { this.poi = poi; @@ -177,8 +179,12 @@ public class LightProbeBlendingProcessor implements SceneProcessor { public void setPoi(Spatial poi) { this.poi = poi; } - - + + @Override + public void setProfiler(AppProfiler profiler) { + this.prof = profiler; + } + private class BlendFactor implements Comparable{ LightProbe lightProbe; diff --git a/jme3-core/src/main/java/com/jme3/material/MatParam.java b/jme3-core/src/main/java/com/jme3/material/MatParam.java index 6c069a14d..b30aab0c5 100644 --- a/jme3-core/src/main/java/com/jme3/material/MatParam.java +++ b/jme3-core/src/main/java/com/jme3/material/MatParam.java @@ -237,7 +237,11 @@ When arrays can be inserted in J3M files Texture texVal = (Texture) value; TextureKey texKey = (TextureKey) texVal.getKey(); if (texKey == null){ - throw new UnsupportedOperationException("The specified MatParam cannot be represented in J3M"); + //throw new UnsupportedOperationException("The specified MatParam cannot be represented in J3M"); + // this is used in toString and the above line causes blender materials to throw this exception. + // toStrings should be very robust IMO as even debuggers often invoke toString and logging code + // often does as well, even implicitly. + return texVal+":returned null key"; } String ret = ""; diff --git a/jme3-core/src/main/java/com/jme3/post/DetailedProfiler.java b/jme3-core/src/main/java/com/jme3/post/DetailedProfiler.java new file mode 100644 index 000000000..409f122bd --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/post/DetailedProfiler.java @@ -0,0 +1,288 @@ +package com.jme3.post; + +import com.jme3.profile.*; +import com.jme3.renderer.Renderer; +import com.jme3.renderer.ViewPort; +import com.jme3.renderer.queue.RenderQueue; + +import java.util.*; + +/** + * Created by Nehon on 25/01/2017. + */ +public class DetailedProfiler implements AppProfiler { + + private final static int MAX_FRAMES = 100; + private Map data; + private Map pool; + private long startFrame; + private static int currentFrame = 0; + private String prevPath = null; + private boolean frameEnded = false; + private Renderer renderer; + private boolean ongoingGpuProfiling = false; + + + private String curAppPath = null; + private String curVpPath = null; + private String curSpPath = null; + private VpStep lastVpStep = null; + + private StringBuilder path = new StringBuilder(256); + private StringBuilder vpPath = new StringBuilder(256); + + private Deque idsPool = new ArrayDeque<>(100); + + StatLine frameTime; + + + @Override + public void appStep(AppStep step) { + curAppPath = step.name(); + + if (step == AppStep.BeginFrame) { + if (data == null) { + data = new LinkedHashMap<>(); + pool = new HashMap<>(); + frameTime = new StatLine(currentFrame); + } + if (frameTime.isActive()) { + frameTime.setValueCpu(System.nanoTime() - frameTime.getValueCpu()); + frameTime.closeFrame(); + + } + frameTime.setNewFrameValueCpu(System.nanoTime()); + + frameEnded = false; + for (StatLine statLine : data.values()) { + for (Iterator i = statLine.taskIds.iterator(); i.hasNext(); ) { + int id = i.next(); + if (renderer.isTaskResultAvailable(id)) { + long val = renderer.getProfilingTime(id); + statLine.setValueGpu(val); + i.remove(); + idsPool.push(id); + } + } + } + data.clear(); + } + + if (data != null) { + String path = getPath(step.name()); + if (step == AppStep.EndFrame) { + if (frameEnded) { + return; + } + addStep(path, System.nanoTime()); + StatLine end = data.get(path); + end.setValueCpu(System.nanoTime() - startFrame); + frameEnded = true; + } else { + addStep(path, System.nanoTime()); + } + } + if (step == AppStep.EndFrame) { + + closeFrame(); + } + } + + private void closeFrame() { + //close frame + if (data != null) { + + prevPath = null; + + for (StatLine statLine : data.values()) { + statLine.closeFrame(); + } + currentFrame++; + } + } + + @Override + public void vpStep(VpStep step, ViewPort vp, RenderQueue.Bucket bucket) { + + if (data != null) { + vpPath.setLength(0); + vpPath.append(vp.getName()).append("/").append((bucket == null ? step.name() : bucket.name() + " Bucket")); + path.setLength(0); + if ((lastVpStep == VpStep.PostQueue || lastVpStep == VpStep.PostFrame) && bucket != null) { + path.append(curAppPath).append("/").append(curVpPath).append(curSpPath).append("/").append(vpPath); + curVpPath = vpPath.toString(); + } else { + if (bucket != null) { + path.append(curAppPath).append("/").append(curVpPath).append("/").append(bucket.name() + " Bucket"); + } else { + path.append(curAppPath).append("/").append(vpPath); + curVpPath = vpPath.toString(); + } + } + lastVpStep = step; + + addStep(path.toString(), System.nanoTime()); + } + } + + @Override + public void spStep(SpStep step, String... additionalInfo) { + + if (data != null) { + curSpPath = getPath("", additionalInfo); + path.setLength(0); + path.append(curAppPath).append("/").append(curVpPath).append(curSpPath); + addStep(path.toString(), System.nanoTime()); + } + + } + + public Map getStats() { + if (data != null) { + return data;//new LinkedHashMap<>(data); + } + return null; + } + + public double getAverageFrameTime() { + return frameTime.getAverageCpu(); + } + + + private void addStep(String path, long value) { + if (ongoingGpuProfiling && renderer != null) { + renderer.stopProfiling(); + ongoingGpuProfiling = false; + } + + if (prevPath != null) { + StatLine prevLine = data.get(prevPath); + if (prevLine != null) { + prevLine.setValueCpu(value - prevLine.getValueCpu()); + } + } + + StatLine line = pool.get(path); + if (line == null) { + line = new StatLine(currentFrame); + pool.put(path, line); + } + data.put(path, line); + line.setNewFrameValueCpu(value); + if (renderer != null) { + int id = getUnusedTaskId(); + line.taskIds.add(id); + renderer.startProfiling(id); + } + ongoingGpuProfiling = true; + prevPath = path; + + } + + private String getPath(String step, String... subPath) { + StringBuilder path = new StringBuilder(step); + if (subPath != null) { + for (String s : subPath) { + path.append("/").append(s); + } + } + return path.toString(); + } + + public void setRenderer(Renderer renderer) { + this.renderer = renderer; + poolTaskIds(renderer); + } + + private void poolTaskIds(Renderer renderer) { + int[] ids = renderer.generateProfilingTasks(100); + for (int id : ids) { + idsPool.push(id); + } + } + + private int getUnusedTaskId() { + if (idsPool.isEmpty()) { + poolTaskIds(renderer); + } + + return idsPool.pop(); + } + + public static class StatLine { + private long[] cpuTimes = new long[MAX_FRAMES]; + private long[] gpuTimes = new long[MAX_FRAMES]; + private int startCursor = 0; + private int cpuCursor = 0; + private int gpuCursor = 0; + private long cpuSum = 0; + private long gpuSum = 0; + private long lastValue = 0; + private int nbFramesCpu; + private int nbFramesGpu; + List taskIds = new ArrayList<>(); + + + private StatLine(int currentFrame) { + startCursor = currentFrame % MAX_FRAMES; + cpuCursor = startCursor; + gpuCursor = startCursor; + } + + private void setNewFrameValueCpu(long value) { + int newCursor = currentFrame % MAX_FRAMES; + if (nbFramesCpu == 0) { + startCursor = newCursor; + } + cpuCursor = newCursor; + lastValue = value; + } + + private void setValueCpu(long val) { + lastValue = val; + } + + private long getValueCpu() { + return lastValue; + } + + private void closeFrame() { + if (isActive()) { + cpuSum -= cpuTimes[cpuCursor]; + cpuTimes[cpuCursor] = lastValue; + cpuSum += lastValue; + nbFramesCpu++; + } else { + nbFramesCpu = 0; + } + } + + public void setValueGpu(long value) { + gpuSum -= gpuTimes[gpuCursor]; + gpuTimes[gpuCursor] = value; + gpuSum += value; + nbFramesGpu++; + gpuCursor = (gpuCursor + 1) % MAX_FRAMES; + } + + public boolean isActive() { + return cpuCursor >= currentFrame % MAX_FRAMES - 1; + } + + public double getAverageCpu() { + if (nbFramesCpu == 0) { + return 0; + } + return (double) cpuSum / (double) Math.min(nbFramesCpu, MAX_FRAMES); + } + + public double getAverageGpu() { + if (nbFramesGpu == 0) { + return 0; + } + + return (double) gpuSum / (double) Math.min(nbFramesGpu, MAX_FRAMES); + } + } + +} diff --git a/jme3-core/src/main/java/com/jme3/post/DetailedProfilerState.java b/jme3-core/src/main/java/com/jme3/post/DetailedProfilerState.java new file mode 100644 index 000000000..249845471 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/post/DetailedProfilerState.java @@ -0,0 +1,448 @@ +package com.jme3.post; + +import com.jme3.app.Application; +import com.jme3.app.SimpleApplication; +import com.jme3.app.state.BaseAppState; +import com.jme3.font.BitmapFont; +import com.jme3.font.BitmapText; +import com.jme3.input.*; +import com.jme3.input.controls.*; +import com.jme3.material.Material; +import com.jme3.material.RenderState; +import com.jme3.math.*; +import com.jme3.profile.AppStep; +import com.jme3.scene.*; +import com.jme3.scene.shape.Quad; + +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.*; + +import static com.jme3.post.DetailedProfiler.*; + +/** + * Created by Nehon on 25/01/2017. + */ +public class DetailedProfilerState extends BaseAppState { + + private static final int PANEL_WIDTH = 400; + private static final int PADDING = 10; + private static final int LINE_HEIGHT = 12; + private static final int HEADER_HEIGHT = 100; + private static final float REFRESH_TIME = 1.0f; + private static final String TOGGLE_KEY = "Toggle_Detailed_Profiler"; + private static final String CLICK_KEY = "Click_Detailed_Profiler"; + private static final String INSIGNIFICANT = "Hide insignificant stat"; + private DetailedProfiler prof = new DetailedProfiler(); + + private float time = 0; + private BitmapFont font; + private BitmapFont bigFont; + private Node ui = new Node("Stats ui"); + private Map lines = new HashMap<>(); + private double totalTimeCpu; + private double totalTimeGpu; + private int maxLevel = 0; + + private BitmapText frameTimeValue; + private BitmapText frameCpuTimeValue; + private BitmapText frameGpuTimeValue; + private BitmapText hideInsignificantField; + + private BitmapText selectedField; + private double selectedValueCpu = 0; + private double selectedValueGpu = 0; + private boolean hideInsignificant = false; + + private StatLineView rootLine; + private int height = 0; + private DecimalFormat df = new DecimalFormat("##0.00", new DecimalFormatSymbols(Locale.US)); + + private ColorRGBA dimmedWhite = ColorRGBA.White.mult(0.7f); + private ColorRGBA dimmedGreen = ColorRGBA.Green.mult(0.7f); + private ColorRGBA dimmedOrange = ColorRGBA.Orange.mult(0.7f); + private ColorRGBA dimmedRed = ColorRGBA.Red.mult(0.7f); + + public DetailedProfilerState() { + + } + + @Override + protected void initialize(Application app) { + Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); + mat.setColor("Color", new ColorRGBA(0, 0, 0, 0.5f)); + mat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha); + Geometry darkenStats = new Geometry("StatsDarken", new Quad(PANEL_WIDTH, app.getCamera().getHeight())); + darkenStats.setMaterial(mat); + darkenStats.setLocalTranslation(0, -app.getCamera().getHeight(), -1); + + ui.attachChild(darkenStats); + ui.setLocalTranslation(app.getCamera().getWidth() - PANEL_WIDTH, app.getCamera().getHeight(), 0); + font = app.getAssetManager().loadFont("Interface/Fonts/Console.fnt"); + bigFont = app.getAssetManager().loadFont("Interface/Fonts/Default.fnt"); + prof.setRenderer(app.getRenderer()); + rootLine = new StatLineView("Frame"); + rootLine.attachTo(ui); + + BitmapText frameLabel = new BitmapText(bigFont); + frameLabel.setText("Total Frame Time: "); + ui.attachChild(frameLabel); + frameLabel.setLocalTranslation(new Vector3f(PANEL_WIDTH / 2 - bigFont.getLineWidth(frameLabel.getText()), -PADDING, 0)); + + BitmapText cpuLabel = new BitmapText(bigFont); + cpuLabel.setText("CPU"); + ui.attachChild(cpuLabel); + cpuLabel.setLocalTranslation(PANEL_WIDTH / 4 - bigFont.getLineWidth(cpuLabel.getText()) / 2, -PADDING - 30, 0); + + BitmapText gpuLabel = new BitmapText(bigFont); + gpuLabel.setText("GPU"); + ui.attachChild(gpuLabel); + gpuLabel.setLocalTranslation(3 * PANEL_WIDTH / 4 - bigFont.getLineWidth(gpuLabel.getText()) / 2, -PADDING - 30, 0); + + frameTimeValue = new BitmapText(bigFont); + frameCpuTimeValue = new BitmapText(bigFont); + frameGpuTimeValue = new BitmapText(bigFont); + + selectedField = new BitmapText(font); + selectedField.setText("Selected: "); + selectedField.setLocalTranslation(PANEL_WIDTH / 2, -PADDING - 75, 0); + selectedField.setColor(ColorRGBA.Yellow); + + + ui.attachChild(frameTimeValue); + ui.attachChild(frameCpuTimeValue); + ui.attachChild(frameGpuTimeValue); + ui.attachChild(selectedField); + + hideInsignificantField = new BitmapText(font); + hideInsignificantField.setText("O " + INSIGNIFICANT); + hideInsignificantField.setLocalTranslation(PADDING, -PADDING - 75, 0); + ui.attachChild(hideInsignificantField); + + final InputManager inputManager = app.getInputManager(); + if (inputManager != null) { + inputManager.addMapping(TOGGLE_KEY, new KeyTrigger(KeyInput.KEY_F6)); + inputManager.addMapping(CLICK_KEY, new MouseButtonTrigger(MouseInput.BUTTON_LEFT)); + inputManager.addListener(new ActionListener() { + @Override + public void onAction(String name, boolean isPressed, float tpf) { + if (name.equals(TOGGLE_KEY) && isPressed) { + setEnabled(!isEnabled()); + } + if (isEnabled() && name.equals(CLICK_KEY) && isPressed) { + handleClick(inputManager.getCursorPosition()); + } + } + }, TOGGLE_KEY, CLICK_KEY); + } + } + + @Override + protected void cleanup(Application app) { + + } + + @Override + public void update(float tpf) { + time += tpf; + } + + private void displayData(Map data) { + if (data == null || data.isEmpty()) { + return; + } + + for (StatLineView statLine : lines.values()) { + statLine.reset(); + statLine.removeFromParent(); + } + rootLine.reset(); + maxLevel = 0; + for (String path : data.keySet()) { + if (path.equals("EndFrame")) { + continue; + } + maxLevel = Math.max(maxLevel, path.split("/").length); + StatLineView line = getStatLineView(path); + StatLine statLine = data.get(path); + line.updateValues(statLine.getAverageCpu(), statLine.getAverageGpu()); + String parent = getParent(path); + while (parent != null) { + StatLineView parentView = getStatLineView(parent); + parentView.updateValues(statLine.getAverageCpu(), statLine.getAverageGpu()); + parentView.children.add(line); + line.attachTo(ui); + line = parentView; + parent = getParent(parent); + } + rootLine.children.add(line); + line.attachTo(ui); + rootLine.updateValues(statLine.getAverageCpu(), statLine.getAverageGpu()); + } + + totalTimeCpu = rootLine.cpuValue; + totalTimeGpu = rootLine.gpuValue + data.get("EndFrame").getAverageGpu(); + + layout(); + + } + + private void layout() { + height = 0; + selectedValueCpu = 0; + selectedValueGpu = 0; + rootLine.layout(0); + + frameTimeValue.setText(df.format(getMsFromNs(prof.getAverageFrameTime())) + "ms"); + frameTimeValue.setLocalTranslation(PANEL_WIDTH / 2, -PADDING, 0); + setColor(frameTimeValue, prof.getAverageFrameTime(), totalTimeCpu, false, false); + + frameCpuTimeValue.setText(df.format(getMsFromNs(totalTimeCpu)) + "ms"); + frameCpuTimeValue.setLocalTranslation(new Vector3f(PANEL_WIDTH / 4 - bigFont.getLineWidth(frameCpuTimeValue.getText()) / 2, -PADDING - 50, 0)); + setColor(frameCpuTimeValue, totalTimeCpu, totalTimeCpu, false, false); + + frameGpuTimeValue.setText(df.format(getMsFromNs(totalTimeGpu)) + "ms"); + frameGpuTimeValue.setLocalTranslation(new Vector3f(3 * PANEL_WIDTH / 4 - bigFont.getLineWidth(frameGpuTimeValue.getText()) / 2, -PADDING - 50, 0)); + setColor(frameGpuTimeValue, totalTimeGpu, totalTimeGpu, false, false); + + selectedField.setText("Selected: " + df.format(getMsFromNs(selectedValueCpu)) + "ms / " + df.format(getMsFromNs(selectedValueGpu)) + "ms"); + + selectedField.setLocalTranslation(3 * PANEL_WIDTH / 4 - font.getLineWidth(selectedField.getText()) / 2, -PADDING - 75, 0); + } + + private StatLineView getStatLineView(String path) { + StatLineView line = lines.get(path); + + if (line == null) { + line = new StatLineView(getLeaf(path)); + lines.put(path, line); + line.attachTo(ui); + } + return line; + } + + private String getLeaf(String path) { + int idx = path.lastIndexOf("/"); + return idx >= 0 ? path.substring(idx + 1) : path; + } + + private String getParent(String path) { + int idx = path.lastIndexOf("/"); + return idx >= 0 ? path.substring(0, idx) : null; + } + + + @Override + public void postRender() { + if (time > REFRESH_TIME) { + prof.appStep(AppStep.EndFrame); + Map data = prof.getStats(); + displayData(data); + time = 0; + } + } + + private double getMsFromNs(double time) { + return time / 1000000.0; + } + + @Override + protected void onEnable() { + getApplication().setAppProfiler(prof); + ((SimpleApplication) getApplication()).getGuiNode().attachChild(ui); + } + + @Override + protected void onDisable() { + getApplication().setAppProfiler(null); + ui.removeFromParent(); + } + + public boolean setColor(BitmapText t, double value, double totalTime, boolean isParent, boolean expended) { + + boolean dimmed = isParent && expended; + boolean insignificant = false; + + if (value > 1000000000.0 / 30.0) { + t.setColor(dimmed ? dimmedRed : ColorRGBA.Red); + } else if (value > 1000000000.0 / 60.0) { + t.setColor(dimmed ? dimmedOrange : ColorRGBA.Orange); + } else if (value > totalTime / 3) { + t.setColor(dimmed ? dimmedGreen : ColorRGBA.Green); + } else if (value < 30000) { + t.setColor(ColorRGBA.DarkGray); + insignificant = true; + } else { + t.setColor(dimmed ? dimmedWhite : ColorRGBA.White); + } + return insignificant; + } + + private void handleClick(Vector2f pos) { + + Vector3f lp = hideInsignificantField.getWorldTranslation(); + float width = font.getLineWidth(hideInsignificantField.getText()); + if (pos.x > lp.x && pos.x < (lp.x + width) + && pos.y < lp.y && pos.y > lp.y - LINE_HEIGHT) { + hideInsignificant = !hideInsignificant; + hideInsignificantField.setText((hideInsignificant ? "X " : "O ") + INSIGNIFICANT); + if (!hideInsignificant) { + rootLine.setExpended(true); + } + } + + rootLine.onClick(pos); + for (StatLineView statLineView : lines.values()) { + statLineView.onClick(pos); + } + layout(); + } + + private class StatLineView { + BitmapText label; + BitmapText cpuText; + BitmapText gpuText; + BitmapText checkBox; + double cpuValue; + double gpuValue; + private boolean expended = true; + private boolean visible = true; + private boolean selected = false; + String text; + + Set children = new LinkedHashSet<>(); + + public StatLineView(String label) { + this.text = label; + this.label = new BitmapText(font); + this.checkBox = new BitmapText(font); + this.checkBox.setText("O"); + this.label.setText("- " + label); + this.cpuText = new BitmapText(font); + this.gpuText = new BitmapText(font); + } + + public void onClick(Vector2f pos) { + + if (!visible) { + return; + } + + Vector3f lp = label.getWorldTranslation(); + Vector3f cp = checkBox.getWorldTranslation(); + if (pos.x > cp.x + && pos.y < lp.y && pos.y > lp.y - LINE_HEIGHT) { + + float width = font.getLineWidth(checkBox.getText()); + if (pos.x >= cp.x && pos.x <= (cp.x + width)) { + selected = !selected; + if (selected) { + checkBox.setText("X"); + } else { + checkBox.setText("O"); + } + } else { + setExpended(!expended); + } + } + } + + public void setExpended(boolean expended) { + this.expended = expended; + if (expended) { + label.setText("- " + text); + } else { + label.setText("+ " + text); + } + for (StatLineView child : children) { + child.setVisible(expended); + } + } + + public void layout(int indent) { + + boolean insignificant; + cpuText.setText(df.format(getMsFromNs(cpuValue)) + "ms /"); + insignificant = setColor(cpuText, cpuValue, totalTimeCpu, !children.isEmpty(), expended); + gpuText.setText(" " + df.format(getMsFromNs(gpuValue)) + "ms"); + insignificant &= setColor(gpuText, gpuValue, totalTimeGpu, !children.isEmpty(), expended); + + if (insignificant && hideInsignificant) { + setVisible(false); + } + + if (!visible) { + return; + } + + if (selected) { + label.setColor(ColorRGBA.Yellow); + selectedValueCpu += cpuValue; + selectedValueGpu += gpuValue; + } else { + label.setColor(ColorRGBA.White); + } + + int y = -(height * LINE_HEIGHT + HEADER_HEIGHT); + + label.setLocalTranslation(PADDING + indent * PADDING, y, 0); + float gpuPos = PANEL_WIDTH - font.getLineWidth(gpuText.getText()) - PADDING * (maxLevel - indent + 1); + cpuText.setLocalTranslation(gpuPos - font.getLineWidth(cpuText.getText()), y, 0); + gpuText.setLocalTranslation(gpuPos, y, 0); + + checkBox.setLocalTranslation(3, y, 0); + height++; + for (StatLineView child : children) { + child.layout(indent + 1); + } + } + + public void updateValues(double cpu, double gpu) { + cpuValue += cpu; + gpuValue += gpu; + } + + public void attachTo(Node node) { + node.attachChild(label); + node.attachChild(cpuText); + node.attachChild(gpuText); + node.attachChild(checkBox); + } + + public void removeFromParent() { + label.removeFromParent(); + cpuText.removeFromParent(); + gpuText.removeFromParent(); + checkBox.removeFromParent(); + } + + public void reset() { + children.clear(); + cpuValue = 0; + gpuValue = 0; + } + + public void setVisible(boolean visible) { + this.visible = visible; + label.setCullHint(visible ? Spatial.CullHint.Dynamic : Spatial.CullHint.Always); + cpuText.setCullHint(visible ? Spatial.CullHint.Dynamic : Spatial.CullHint.Always); + gpuText.setCullHint(visible ? Spatial.CullHint.Dynamic : Spatial.CullHint.Always); + checkBox.setCullHint(visible ? Spatial.CullHint.Dynamic : Spatial.CullHint.Always); + + + for (StatLineView child : children) { + child.setVisible(visible && expended); + } + + } + + + @Override + public String toString() { + return label.getText() + " - " + df.format(getMsFromNs(cpuValue)) + "ms / " + df.format(getMsFromNs(gpuValue)) + "ms"; + } + + + } +} + diff --git a/jme3-core/src/main/java/com/jme3/post/Filter.java b/jme3-core/src/main/java/com/jme3/post/Filter.java index feaacbe5d..b162ed511 100644 --- a/jme3-core/src/main/java/com/jme3/post/Filter.java +++ b/jme3-core/src/main/java/com/jme3/post/Filter.java @@ -85,6 +85,14 @@ public abstract class Filter implements Savable { protected Texture2D renderedTexture; protected Texture2D depthTexture; protected Material passMaterial; + protected String name; + + public Pass(String name) { + this.name = name; + } + + public Pass() { + } /** * init the pass called internally @@ -197,6 +205,11 @@ public abstract class Filter implements Savable { depthTexture.getImage().dispose(); } } + + @Override + public String toString() { + return name == null ? super.toString() : name; + } } /** diff --git a/jme3-core/src/main/java/com/jme3/post/FilterPostProcessor.java b/jme3-core/src/main/java/com/jme3/post/FilterPostProcessor.java index fc7fdd6bf..7ccbbe5ae 100644 --- a/jme3-core/src/main/java/com/jme3/post/FilterPostProcessor.java +++ b/jme3-core/src/main/java/com/jme3/post/FilterPostProcessor.java @@ -34,6 +34,7 @@ package com.jme3.post; import com.jme3.asset.AssetManager; import com.jme3.export.*; import com.jme3.material.Material; +import com.jme3.profile.*; import com.jme3.renderer.*; import com.jme3.renderer.queue.RenderQueue; import com.jme3.texture.FrameBuffer; @@ -56,6 +57,7 @@ import java.util.List; */ public class FilterPostProcessor implements SceneProcessor, Savable { + public static final String FPP = FilterPostProcessor.class.getSimpleName(); private RenderManager renderManager; private Renderer renderer; private ViewPort viewPort; @@ -80,6 +82,7 @@ public class FilterPostProcessor implements SceneProcessor, Savable { private int lastFilterIndex = -1; private boolean cameraInit = false; private boolean multiView = false; + private AppProfiler prof; private Format fbFormat = Format.RGB111110F; @@ -216,7 +219,6 @@ public class FilterPostProcessor implements SceneProcessor, Savable { r.setFrameBuffer(buff); r.clearBuffers(true, true, true); renderManager.renderGeometry(fsQuad); - } public boolean isInitialized() { @@ -224,13 +226,12 @@ public class FilterPostProcessor implements SceneProcessor, Savable { } public void postQueue(RenderQueue rq) { - for (Filter filter : filters.getArray()) { if (filter.isEnabled()) { + if (prof != null) prof.spStep(SpStep.ProcPostQueue, FPP, filter.getName()); filter.postQueue(rq); } } - } /** @@ -244,10 +245,12 @@ public class FilterPostProcessor implements SceneProcessor, Savable { boolean msDepth = depthTexture != null && depthTexture.getImage().getMultiSamples() > 1; for (int i = 0; i < filters.size(); i++) { Filter filter = filters.get(i); + if (prof != null) prof.spStep(SpStep.ProcPostFrame, FPP, filter.getName()); if (filter.isEnabled()) { if (filter.getPostRenderPasses() != null) { for (Iterator it1 = filter.getPostRenderPasses().iterator(); it1.hasNext();) { Filter.Pass pass = it1.next(); + if (prof != null) prof.spStep(SpStep.ProcPostFrame, FPP, filter.getName(), pass.toString()); pass.beforeRender(); if (pass.requiresSceneAsTexture()) { pass.getPassMaterial().setTexture("Texture", tex); @@ -269,7 +272,7 @@ public class FilterPostProcessor implements SceneProcessor, Savable { renderProcessing(r, pass.getRenderFrameBuffer(), pass.getPassMaterial()); } } - + if (prof != null) prof.spStep(SpStep.ProcPostFrame, FPP, filter.getName(), "postFrame"); filter.postFrame(renderManager, viewPort, buff, sceneFb); Material mat = filter.getMaterial(); @@ -298,7 +301,9 @@ public class FilterPostProcessor implements SceneProcessor, Savable { tex = filter.getRenderedTexture(); } + if (prof != null) prof.spStep(SpStep.ProcPostFrame, FPP, filter.getName(), "render"); renderProcessing(r, buff, mat); + if (prof != null) prof.spStep(SpStep.ProcPostFrame, FPP, filter.getName(), "postFilter"); filter.postFilter(r, buff); if (wantsBilinear) { @@ -324,7 +329,6 @@ public class FilterPostProcessor implements SceneProcessor, Savable { if (viewPort != null) { renderManager.setCamera(viewPort.getCamera(), false); } - } public void preFrame(float tpf) { @@ -351,6 +355,7 @@ public class FilterPostProcessor implements SceneProcessor, Savable { for (Filter filter : filters.getArray()) { if (filter.isEnabled()) { + if (prof != null) prof.spStep(SpStep.ProcPreFrame, FPP, filter.getName()); filter.preFrame(tpf); } } @@ -419,6 +424,11 @@ public class FilterPostProcessor implements SceneProcessor, Savable { } + @Override + public void setProfiler(AppProfiler profiler) { + this.prof = profiler; + } + public void reshape(ViewPort vp, int w, int h) { Camera cam = vp.getCamera(); //this has no effect at first init but is useful when resizing the canvas with multi views diff --git a/jme3-core/src/main/java/com/jme3/post/HDRRenderer.java b/jme3-core/src/main/java/com/jme3/post/HDRRenderer.java index 06482f21f..424f3b689 100644 --- a/jme3-core/src/main/java/com/jme3/post/HDRRenderer.java +++ b/jme3-core/src/main/java/com/jme3/post/HDRRenderer.java @@ -34,6 +34,7 @@ package com.jme3.post; import com.jme3.asset.AssetManager; import com.jme3.material.Material; import com.jme3.math.Vector2f; +import com.jme3.profile.AppProfiler; import com.jme3.renderer.*; import com.jme3.renderer.queue.RenderQueue; import com.jme3.texture.FrameBuffer; @@ -61,6 +62,7 @@ public class HDRRenderer implements SceneProcessor { private RenderManager renderManager; private ViewPort viewPort; private static final Logger logger = Logger.getLogger(HDRRenderer.class.getName()); + private AppProfiler prof; private Camera fbCam = new Camera(1, 1); @@ -418,4 +420,9 @@ public class HDRRenderer implements SceneProcessor { } } + @Override + public void setProfiler(AppProfiler profiler) { + this.prof = profiler; + } + } diff --git a/jme3-core/src/main/java/com/jme3/post/PreDepthProcessor.java b/jme3-core/src/main/java/com/jme3/post/PreDepthProcessor.java index 3b318b514..a3125dd1e 100644 --- a/jme3-core/src/main/java/com/jme3/post/PreDepthProcessor.java +++ b/jme3-core/src/main/java/com/jme3/post/PreDepthProcessor.java @@ -35,6 +35,7 @@ import com.jme3.asset.AssetManager; import com.jme3.material.Material; import com.jme3.material.RenderState; import com.jme3.material.RenderState.FaceCullMode; +import com.jme3.profile.AppProfiler; import com.jme3.renderer.RenderManager; import com.jme3.renderer.ViewPort; import com.jme3.renderer.queue.RenderQueue; @@ -51,6 +52,7 @@ public class PreDepthProcessor implements SceneProcessor { private AssetManager assetManager; private Material preDepth; private RenderState forcedRS; + private AppProfiler prof; public PreDepthProcessor(AssetManager assetManager){ this.assetManager = assetManager; @@ -96,4 +98,9 @@ public class PreDepthProcessor implements SceneProcessor { vp = null; } + @Override + public void setProfiler(AppProfiler profiler) { + this.prof = profiler; + } + } diff --git a/jme3-core/src/main/java/com/jme3/post/SceneProcessor.java b/jme3-core/src/main/java/com/jme3/post/SceneProcessor.java index 4f90e52ff..3a8ff1689 100644 --- a/jme3-core/src/main/java/com/jme3/post/SceneProcessor.java +++ b/jme3-core/src/main/java/com/jme3/post/SceneProcessor.java @@ -31,6 +31,7 @@ */ package com.jme3.post; +import com.jme3.profile.AppProfiler; import com.jme3.renderer.RenderManager; import com.jme3.renderer.ViewPort; import com.jme3.renderer.queue.RenderQueue; @@ -90,4 +91,11 @@ public interface SceneProcessor { */ public void cleanup(); + /** + * Sets a profiler Instance for this processor. + * + * @param profiler the profiler instance. + */ + public void setProfiler(AppProfiler profiler); + } diff --git a/jme3-core/src/main/java/com/jme3/profile/AppProfiler.java b/jme3-core/src/main/java/com/jme3/profile/AppProfiler.java index 85ab1e74c..4cfe0b92f 100644 --- a/jme3-core/src/main/java/com/jme3/profile/AppProfiler.java +++ b/jme3-core/src/main/java/com/jme3/profile/AppProfiler.java @@ -57,7 +57,12 @@ public interface AppProfiler { * steps the Bucket parameter will be non-null. */ public void vpStep(VpStep step, ViewPort vp, Bucket bucket); - + + /** + * Called at the beginning of the specified SpStep (SceneProcessor step). + * For more detailed steps it is possible to provide additional information as strings, like the name of the processor. + */ + public void spStep(SpStep step, String... additionalInfo); } diff --git a/jme3-core/src/main/java/com/jme3/profile/SpStep.java b/jme3-core/src/main/java/com/jme3/profile/SpStep.java new file mode 100644 index 000000000..13d25d976 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/profile/SpStep.java @@ -0,0 +1,13 @@ +package com.jme3.profile; + +/** + * Indicates a scene processor-level step within the profiled + * frame. + *

+ * Created by Nehon on 25/01/2017. + */ +public enum SpStep { + ProcPreFrame, + ProcPostQueue, + ProcPostFrame, +} diff --git a/jme3-core/src/main/java/com/jme3/profile/VpStep.java b/jme3-core/src/main/java/com/jme3/profile/VpStep.java index c295be3af..9dff85368 100644 --- a/jme3-core/src/main/java/com/jme3/profile/VpStep.java +++ b/jme3-core/src/main/java/com/jme3/profile/VpStep.java @@ -42,9 +42,11 @@ package com.jme3.profile; public enum VpStep { BeginRender, RenderScene, + PreFrame, PostQueue, FlushQueue, PostFrame, + ProcEndRender, RenderBucket, EndRender } diff --git a/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java b/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java index 5d8265fcb..950f1a4a6 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java +++ b/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java @@ -42,9 +42,7 @@ import com.jme3.material.Technique; import com.jme3.material.TechniqueDef; import com.jme3.math.*; import com.jme3.post.SceneProcessor; -import com.jme3.profile.AppProfiler; -import com.jme3.profile.AppStep; -import com.jme3.profile.VpStep; +import com.jme3.profile.*; import com.jme3.renderer.queue.GeometryList; import com.jme3.renderer.queue.RenderQueue; import com.jme3.renderer.queue.RenderQueue.Bucket; @@ -1070,10 +1068,13 @@ public class RenderManager { } if (processors != null) { + if (prof != null) prof.vpStep(VpStep.PreFrame, vp, null); for (SceneProcessor proc : processors.getArray()) { if (!proc.isInitialized()) { proc.initialize(this, vp); } + proc.setProfiler(this.prof); + if (prof != null) prof.spStep(SpStep.ProcPreFrame, proc.getClass().getSimpleName()); proc.preFrame(tpf); } } @@ -1098,6 +1099,7 @@ public class RenderManager { if (processors != null) { if (prof!=null) prof.vpStep(VpStep.PostQueue, vp, null); for (SceneProcessor proc : processors.getArray()) { + if (prof != null) prof.spStep(SpStep.ProcPostQueue, proc.getClass().getSimpleName()); proc.postQueue(vp.getQueue()); } } @@ -1108,14 +1110,16 @@ public class RenderManager { if (processors != null) { if (prof!=null) prof.vpStep(VpStep.PostFrame, vp, null); for (SceneProcessor proc : processors.getArray()) { + if (prof != null) prof.spStep(SpStep.ProcPostFrame, proc.getClass().getSimpleName()); proc.postFrame(vp.getOutputFrameBuffer()); } + if (prof != null) prof.vpStep(VpStep.ProcEndRender, vp, null); } //renders the translucent objects queue after processors have been rendered renderTranslucentQueue(vp); // clear any remaining spatials that were not rendered. clearQueue(vp); - + if (prof!=null) prof.vpStep(VpStep.EndRender, vp, null); } diff --git a/jme3-core/src/main/java/com/jme3/renderer/Renderer.java b/jme3-core/src/main/java/com/jme3/renderer/Renderer.java index d6645d24d..9a4e5bcc2 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/Renderer.java +++ b/jme3-core/src/main/java/com/jme3/renderer/Renderer.java @@ -387,4 +387,46 @@ public interface Renderer { */ public void setLinearizeSrgbImages(boolean linearize); + + /** + * Generates a pool of gpu queries meant to use as profiling tasks + * + * @param numTasks the number of task ids to generate + * @return an array of tasks ids. + */ + public int[] generateProfilingTasks(int numTasks); + + /** + * Starts a time profiling task on the GPU. + * This will profile all operations called between startProfiling and stopProfiling + * + * @param taskId the id of the task to start profiling. + */ + public void startProfiling(int taskId); + + /** + * Will stop the last profiling task started with startProfiling + */ + public void stopProfiling(); + + /** + * Returns the time in nano seconds elapsed for the task with the given id. + * Note that the result may not be available right after stopProfiling has been called. + * You need to check if the result is available with isTaskResultAvailable. + * Also note that it's guaranteed that the result will be available on next frame. + * If you use getProfilingTime on the next frame you called stopProfiling, you don't need to check the result availability with isTaskResultAvailable + * + * @param taskId the id of the task given by startProfiling. + * @return the time in nano second of the profiling task with the given id. + */ + public long getProfilingTime(int taskId); + + /** + * Check if the profiling results are available + * + * @param taskId the id of the task provided by startProfiling + * @return true if the resulst of the task with the given task id are available. + */ + public boolean isTaskResultAvailable(int taskId); + } diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GL.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GL.java index 136442f77..5aa2cdb83 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GL.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GL.java @@ -126,6 +126,8 @@ public interface GL { public static final int GL_OUT_OF_MEMORY = 0x505; public static final int GL_POINTS = 0x0; public static final int GL_POLYGON_OFFSET_FILL = 0x8037; + public static final int GL_QUERY_RESULT = 0x8866; + public static final int GL_QUERY_RESULT_AVAILABLE = 0x8867; public static final int GL_RED = 0x1903; public static final int GL_RENDERER = 0x1F01; public static final int GL_REPEAT = 0x2901; @@ -177,6 +179,7 @@ public interface GL { public static final int GL_TEXTURE_MIN_FILTER = 0x2801; public static final int GL_TEXTURE_WRAP_S = 0x2802; public static final int GL_TEXTURE_WRAP_T = 0x2803; + public static final int GL_TIME_ELAPSED = 0x88BF; public static final int GL_TRIANGLES = 0x4; public static final int GL_TRIANGLE_FAN = 0x6; public static final int GL_TRIANGLE_STRIP = 0x5; @@ -196,6 +199,7 @@ public interface GL { public void glActiveTexture(int texture); public void glAttachShader(int program, int shader); + public void glBeginQuery(int target, int query); public void glBindBuffer(int target, int buffer); public void glBindTexture(int target, int texture); public void glBlendEquationSeparate(int colorMode, int alphaMode); @@ -232,8 +236,10 @@ public interface GL { public void glDrawRangeElements(int mode, int start, int end, int count, int type, long indices); /// GL2+ public void glEnable(int cap); public void glEnableVertexAttribArray(int index); + public void glEndQuery(int target); public void glGenBuffers(IntBuffer buffers); public void glGenTextures(IntBuffer textures); + public void glGenQueries(int number, IntBuffer ids); public int glGetAttribLocation(int program, String name); public void glGetBoolean(int pname, ByteBuffer params); public void glGetBufferSubData(int target, long offset, ByteBuffer data); @@ -241,7 +247,9 @@ public interface GL { public void glGetInteger(int pname, IntBuffer params); public void glGetProgram(int program, int pname, IntBuffer params); public String glGetProgramInfoLog(int program, int maxSize); - public void glGetShader(int shader, int pname, IntBuffer params); + public long glGetQueryObjectui64(int query, int pname); + public int glGetQueryObjectiv(int query, int pname); + public void glGetShader(int shader, int pname, IntBuffer params); public String glGetShaderInfoLog(int shader, int maxSize); public String glGetString(int name); public int glGetUniformLocation(int program, String name); diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLDebugES.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLDebugES.java index 6b15bb967..412d738f5 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLDebugES.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLDebugES.java @@ -30,6 +30,12 @@ public class GLDebugES extends GLDebug implements GL, GLFbo, GLExt { checkError(); } + @Override + public void glBeginQuery(int target, int query) { + gl.glBeginQuery(target, query); + checkError(); + } + public void glBindBuffer(int target, int buffer) { gl.glBindBuffer(target, buffer); checkError(); @@ -198,6 +204,11 @@ public class GLDebugES extends GLDebug implements GL, GLFbo, GLExt { checkError(); } + @Override + public void glEndQuery(int target) { + checkError(); + } + public void glGenBuffers(IntBuffer buffers) { gl.glGenBuffers(buffers); checkError(); @@ -208,6 +219,12 @@ public class GLDebugES extends GLDebug implements GL, GLFbo, GLExt { checkError(); } + @Override + public void glGenQueries(int num, IntBuffer ids) { + glGenQueries(num, ids); + checkError(); + } + public int glGetAttribLocation(int program, String name) { int location = gl.glGetAttribLocation(program, name); checkError(); @@ -240,6 +257,20 @@ public class GLDebugES extends GLDebug implements GL, GLFbo, GLExt { return infoLog; } + @Override + public long glGetQueryObjectui64(int query, int pname) { + long res = gl.glGetQueryObjectui64(query, pname); + checkError(); + return res; + } + + @Override + public int glGetQueryObjectiv(int query, int pname) { + int res = gl.glGetQueryObjectiv(query, pname); + checkError(); + return res; + } + public void glGetShader(int shader, int pname, IntBuffer params) { gl.glGetShader(shader, pname, params); checkError(); diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java index 943d3ab40..2eedf2c50 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java @@ -2851,4 +2851,31 @@ public final class GLRenderer implements Renderer { linearizeSrgbImages = linearize; } } + + @Override + public int[] generateProfilingTasks(int numTasks) { + IntBuffer ids = BufferUtils.createIntBuffer(numTasks); + gl.glGenQueries(numTasks, ids); + return BufferUtils.getIntArray(ids); + } + + @Override + public void startProfiling(int taskId) { + gl.glBeginQuery(GL.GL_TIME_ELAPSED, taskId); + } + + @Override + public void stopProfiling() { + gl.glEndQuery(GL.GL_TIME_ELAPSED); + } + + @Override + public long getProfilingTime(int taskId) { + return gl.glGetQueryObjectui64(taskId, GL.GL_QUERY_RESULT); + } + + @Override + public boolean isTaskResultAvailable(int taskId) { + return gl.glGetQueryObjectiv(taskId, GL.GL_QUERY_RESULT_AVAILABLE) == 1; + } } diff --git a/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowRenderer.java b/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowRenderer.java index dff4da90d..b390c1340 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowRenderer.java +++ b/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowRenderer.java @@ -44,6 +44,7 @@ import com.jme3.math.Matrix4f; import com.jme3.math.Vector2f; import com.jme3.math.Vector3f; import com.jme3.post.SceneProcessor; +import com.jme3.profile.AppProfiler; import com.jme3.renderer.Camera; import com.jme3.renderer.RenderManager; import com.jme3.renderer.Renderer; @@ -99,6 +100,7 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable, protected Picture[] dispPic; protected RenderState forcedRenderState = new RenderState(); protected Boolean renderBackFacesShadows = true; + protected AppProfiler prof; /** * true if the fallback material should be used, otherwise false @@ -807,6 +809,10 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable, init(assetManager, nbShadowMaps, (int) shadowMapSize); } + public void setProfiler(AppProfiler profiler) { + this.prof = profiler; + } + /** * De-serialize this instance, for example when loading from a J3O file. * diff --git a/jme3-core/src/main/java/com/jme3/shadow/BasicShadowRenderer.java b/jme3-core/src/main/java/com/jme3/shadow/BasicShadowRenderer.java index 1410574ea..4beb4000c 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/BasicShadowRenderer.java +++ b/jme3-core/src/main/java/com/jme3/shadow/BasicShadowRenderer.java @@ -35,6 +35,7 @@ import com.jme3.asset.AssetManager; import com.jme3.material.Material; import com.jme3.math.Vector3f; import com.jme3.post.SceneProcessor; +import com.jme3.profile.AppProfiler; import com.jme3.renderer.Camera; import com.jme3.renderer.RenderManager; import com.jme3.renderer.Renderer; @@ -75,7 +76,8 @@ public class BasicShadowRenderer implements SceneProcessor { protected GeometryList lightReceivers = new GeometryList(new OpaqueComparator()); protected GeometryList shadowOccluders = new GeometryList(new OpaqueComparator()); - + private AppProfiler prof; + /** * Creates a BasicShadowRenderer * @param manager the asset manager @@ -221,6 +223,11 @@ public class BasicShadowRenderer implements SceneProcessor { public void cleanup() { } + @Override + public void setProfiler(AppProfiler profiler) { + this.prof = profiler; + } + public void reshape(ViewPort vp, int w, int h) { dispPic.setPosition(w / 20f, h / 20f); dispPic.setWidth(w / 5f); diff --git a/jme3-core/src/main/java/com/jme3/shadow/PssmShadowRenderer.java b/jme3-core/src/main/java/com/jme3/shadow/PssmShadowRenderer.java index bf6362d62..4b854ec37 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/PssmShadowRenderer.java +++ b/jme3-core/src/main/java/com/jme3/shadow/PssmShadowRenderer.java @@ -38,6 +38,7 @@ import com.jme3.math.Matrix4f; import com.jme3.math.Vector2f; import com.jme3.math.Vector3f; import com.jme3.post.SceneProcessor; +import com.jme3.profile.AppProfiler; import com.jme3.renderer.Camera; import com.jme3.renderer.Caps; import com.jme3.renderer.RenderManager; @@ -76,6 +77,8 @@ import java.util.List; @Deprecated public class PssmShadowRenderer implements SceneProcessor { + private AppProfiler prof; + /** * FilterMode specifies how shadows are filtered * @deprecated use {@link EdgeFilteringMode} @@ -725,6 +728,11 @@ public class PssmShadowRenderer implements SceneProcessor { } } + @Override + public void setProfiler(AppProfiler profiler) { + this.prof = profiler; + } + /** * get the length over which the shadow will fade out when using a * shadowZextend diff --git a/jme3-core/src/main/java/com/jme3/system/NullRenderer.java b/jme3-core/src/main/java/com/jme3/system/NullRenderer.java index 293c5e2c7..1eb2b1533 100644 --- a/jme3-core/src/main/java/com/jme3/system/NullRenderer.java +++ b/jme3-core/src/main/java/com/jme3/system/NullRenderer.java @@ -172,6 +172,31 @@ public class NullRenderer implements Renderer { public void setLinearizeSrgbImages(boolean linearize) { } + @Override + public int[] generateProfilingTasks(int numTasks) { + return new int[0]; + } + + @Override + public void startProfiling(int id) { + + } + + @Override + public void stopProfiling() { + + } + + @Override + public long getProfilingTime(int taskId) { + return 0; + } + + @Override + public boolean isTaskResultAvailable(int taskId) { + return false; + } + public void readFrameBufferWithFormat(FrameBuffer fb, ByteBuffer byteBuf, Image.Format format) { } diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag b/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag index 2841ed8e3..9ce4e421b 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag +++ b/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag @@ -148,18 +148,6 @@ void main(){ vec3 normal = normalize(wNormal); #endif - - #ifdef LIGHTMAP - vec3 lightMapColor; - #ifdef SEPARATE_TEXCOORD - lightMapColor = texture2D(m_LightMap, texCoord2).rgb; - #else - lightMapColor = texture2D(m_LightMap, texCoord).rgb; - #endif - specularColor.rgb *= lightMapColor; - albedo.rgb *= lightMapColor; - #endif - float specular = 0.5; #ifdef SPECGLOSSPIPELINE vec4 specularColor = texture2D(m_SpecularMap, newTexCoord); @@ -171,6 +159,17 @@ void main(){ vec4 diffuseColor = albedo - albedo * Metallic; #endif + #ifdef LIGHTMAP + vec3 lightMapColor; + #ifdef SEPARATE_TEXCOORD + lightMapColor = texture2D(m_LightMap, texCoord2).rgb; + #else + lightMapColor = texture2D(m_LightMap, texCoord).rgb; + #endif + specularColor.rgb *= lightMapColor; + albedo.rgb *= lightMapColor; + #endif + gl_FragColor.rgb = vec3(0.0); float ndotv = max( dot( normal, viewDir ),0.0); for( int i = 0;i < NB_LIGHTS; i+=3){ diff --git a/jme3-desktop/src/main/java/com/jme3/app/state/VideoRecorderAppState.java b/jme3-desktop/src/main/java/com/jme3/app/state/VideoRecorderAppState.java index ccaecc580..90b9e98cf 100644 --- a/jme3-desktop/src/main/java/com/jme3/app/state/VideoRecorderAppState.java +++ b/jme3-desktop/src/main/java/com/jme3/app/state/VideoRecorderAppState.java @@ -33,6 +33,7 @@ package com.jme3.app.state; import com.jme3.app.Application; import com.jme3.post.SceneProcessor; +import com.jme3.profile.AppProfiler; import com.jme3.renderer.Camera; import com.jme3.renderer.RenderManager; import com.jme3.renderer.Renderer; @@ -220,6 +221,7 @@ public class VideoRecorderAppState extends AbstractAppState { private LinkedBlockingQueue freeItems; private LinkedBlockingQueue usedItems = new LinkedBlockingQueue(); private MjpegFileWriter writer; + private AppProfiler prof; public void addImage(Renderer renderer, FrameBuffer out) { if (freeItems == null) { @@ -298,6 +300,11 @@ public class VideoRecorderAppState extends AbstractAppState { } writer = null; } + + @Override + public void setProfiler(AppProfiler profiler) { + this.prof = profiler; + } } public static final class IsoTimer extends com.jme3.system.Timer { diff --git a/jme3-desktop/src/main/java/com/jme3/system/awt/AwtPanel.java b/jme3-desktop/src/main/java/com/jme3/system/awt/AwtPanel.java index 8c472bda1..f77eb7336 100644 --- a/jme3-desktop/src/main/java/com/jme3/system/awt/AwtPanel.java +++ b/jme3-desktop/src/main/java/com/jme3/system/awt/AwtPanel.java @@ -32,6 +32,7 @@ package com.jme3.system.awt; import com.jme3.post.SceneProcessor; +import com.jme3.profile.AppProfiler; import com.jme3.renderer.RenderManager; import com.jme3.renderer.ViewPort; import com.jme3.renderer.queue.RenderQueue; @@ -79,6 +80,7 @@ public class AwtPanel extends Canvas implements SceneProcessor { private int newHeight = 1; private AtomicBoolean reshapeNeeded = new AtomicBoolean(false); private final Object lock = new Object(); + private AppProfiler prof; public AwtPanel(PaintMode paintMode) { this(paintMode, false); @@ -333,4 +335,9 @@ public class AwtPanel extends Canvas implements SceneProcessor { @Override public void cleanup() { } + + @Override + public void setProfiler(AppProfiler profiler) { + this.prof = profiler; + } } diff --git a/jme3-effects/src/main/java/com/jme3/post/ssao/SSAOFilter.java b/jme3-effects/src/main/java/com/jme3/post/ssao/SSAOFilter.java index 860b834df..3645bfd36 100644 --- a/jme3-effects/src/main/java/com/jme3/post/ssao/SSAOFilter.java +++ b/jme3-effects/src/main/java/com/jme3/post/ssao/SSAOFilter.java @@ -156,7 +156,7 @@ public class SSAOFilter extends Filter { random.setWrap(Texture.WrapMode.Repeat); ssaoMat.setTexture("RandomMap", random); - ssaoPass = new Pass() { + ssaoPass = new Pass("SSAO pass") { @Override public boolean requiresDepthAsTexture() { diff --git a/jme3-effects/src/main/java/com/jme3/water/ReflectionProcessor.java b/jme3-effects/src/main/java/com/jme3/water/ReflectionProcessor.java index 6307c474e..a3c12e4e8 100644 --- a/jme3-effects/src/main/java/com/jme3/water/ReflectionProcessor.java +++ b/jme3-effects/src/main/java/com/jme3/water/ReflectionProcessor.java @@ -33,6 +33,7 @@ package com.jme3.water; import com.jme3.math.Plane; import com.jme3.post.SceneProcessor; +import com.jme3.profile.AppProfiler; import com.jme3.renderer.Camera; import com.jme3.renderer.RenderManager; import com.jme3.renderer.ViewPort; @@ -50,6 +51,7 @@ public class ReflectionProcessor implements SceneProcessor { private Camera reflectionCam; private FrameBuffer reflectionBuffer; private Plane reflectionClipPlane; + private AppProfiler prof; /** * Creates a ReflectionProcessor @@ -100,6 +102,11 @@ public class ReflectionProcessor implements SceneProcessor { public void cleanup() { } + @Override + public void setProfiler(AppProfiler profiler) { + this.prof = profiler; + } + /** * Internal use only
* returns the frame buffer diff --git a/jme3-effects/src/main/java/com/jme3/water/SimpleWaterProcessor.java b/jme3-effects/src/main/java/com/jme3/water/SimpleWaterProcessor.java index 996720904..7f07cd550 100644 --- a/jme3-effects/src/main/java/com/jme3/water/SimpleWaterProcessor.java +++ b/jme3-effects/src/main/java/com/jme3/water/SimpleWaterProcessor.java @@ -35,21 +35,16 @@ import com.jme3.asset.AssetManager; import com.jme3.material.Material; import com.jme3.math.*; import com.jme3.post.SceneProcessor; -import com.jme3.renderer.Camera; -import com.jme3.renderer.RenderManager; -import com.jme3.renderer.Renderer; -import com.jme3.renderer.ViewPort; +import com.jme3.profile.AppProfiler; +import com.jme3.renderer.*; import com.jme3.renderer.queue.RenderQueue; import com.jme3.scene.Geometry; import com.jme3.scene.Spatial; import com.jme3.scene.shape.Quad; -import com.jme3.texture.FrameBuffer; +import com.jme3.texture.*; import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture; import com.jme3.texture.Texture.WrapMode; -import com.jme3.texture.Texture2D; import com.jme3.ui.Picture; -import com.jme3.util.TempVars; /** * @@ -125,8 +120,9 @@ public class SimpleWaterProcessor implements SceneProcessor { private float distortionScale = 0.2f; private float distortionMix = 0.5f; private float texScale = 1f; - - + private AppProfiler prof; + + /** * Creates a SimpleWaterProcessor * @param manager the asset manager @@ -222,6 +218,11 @@ public class SimpleWaterProcessor implements SceneProcessor { public void cleanup() { } + @Override + public void setProfiler(AppProfiler profiler) { + this.prof = profiler; + } + //debug only : displays maps protected void displayMap(Renderer r, Picture pic, int left) { Camera cam = vp.getCamera(); @@ -585,6 +586,7 @@ public class SimpleWaterProcessor implements SceneProcessor { RenderManager rm; ViewPort vp; + private AppProfiler prof; public void initialize(RenderManager rm, ViewPort vp) { this.rm = rm; @@ -611,5 +613,10 @@ public class SimpleWaterProcessor implements SceneProcessor { public void cleanup() { } + + @Override + public void setProfiler(AppProfiler profiler) { + this.prof = profiler; + } } } diff --git a/jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssao.frag b/jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssao.frag index 6575b119b..460e96f4b 100644 --- a/jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssao.frag +++ b/jme3-effects/src/main/resources/Common/MatDefs/SSAO/ssao.frag @@ -33,8 +33,8 @@ vec3 getPosition(float depthv, in vec2 uv){ vec3 approximateNormal(in vec3 pos,in vec2 texCoord){ float step = g_ResolutionInverse.x ; float stepy = g_ResolutionInverse.y ; - float depth2 = texture2D(m_DepthTexture,texCoord + vec2(step,-stepy)).r; - float depth3 = texture2D(m_DepthTexture,texCoord + vec2(-step,-stepy)).r; + float depth2 = getDepth(m_DepthTexture,texCoord + vec2(step,-stepy)).r; + float depth3 = getDepth(m_DepthTexture,texCoord + vec2(-step,-stepy)).r; vec3 pos2 = vec3(getPosition(depth2,texCoord + vec2(step,-stepy))); vec3 pos3 = vec3(getPosition(depth3,texCoord + vec2(-step,-stepy))); diff --git a/jme3-examples/src/main/java/jme3test/post/SSAOUI.java b/jme3-examples/src/main/java/jme3test/post/SSAOUI.java index 3314d2c7b..c736b2598 100644 --- a/jme3-examples/src/main/java/jme3test/post/SSAOUI.java +++ b/jme3-examples/src/main/java/jme3test/post/SSAOUI.java @@ -52,7 +52,7 @@ public class SSAOUI { } private void init(InputManager inputManager) { - System.out.println("----------------- Water UI Debugger --------------------"); + System.out.println("----------------- SSAO UI Debugger --------------------"); System.out.println("-- Sample Radius : press Y to increase, H to decrease"); System.out.println("-- AO Intensity : press U to increase, J to decrease"); System.out.println("-- AO scale : press I to increase, K to decrease"); diff --git a/jme3-examples/src/main/java/jme3test/post/TestMultiRenderTarget.java b/jme3-examples/src/main/java/jme3test/post/TestMultiRenderTarget.java index 797d238dc..ccb073a33 100644 --- a/jme3-examples/src/main/java/jme3test/post/TestMultiRenderTarget.java +++ b/jme3-examples/src/main/java/jme3test/post/TestMultiRenderTarget.java @@ -41,6 +41,7 @@ import com.jme3.math.Matrix4f; import com.jme3.math.Quaternion; import com.jme3.math.Vector3f; import com.jme3.post.SceneProcessor; +import com.jme3.profile.AppProfiler; import com.jme3.renderer.RenderManager; import com.jme3.renderer.ViewPort; import com.jme3.renderer.queue.RenderQueue; @@ -231,4 +232,9 @@ public class TestMultiRenderTarget extends SimpleApplication implements ScenePro public void cleanup() { } + @Override + public void setProfiler(AppProfiler profiler) { + + } + } diff --git a/jme3-examples/src/main/java/jme3test/post/TestRenderToMemory.java b/jme3-examples/src/main/java/jme3test/post/TestRenderToMemory.java index 17b22d167..dd59a1d1b 100644 --- a/jme3-examples/src/main/java/jme3test/post/TestRenderToMemory.java +++ b/jme3-examples/src/main/java/jme3test/post/TestRenderToMemory.java @@ -39,6 +39,7 @@ import com.jme3.math.FastMath; import com.jme3.math.Quaternion; import com.jme3.math.Vector3f; import com.jme3.post.SceneProcessor; +import com.jme3.profile.AppProfiler; import com.jme3.renderer.Camera; import com.jme3.renderer.RenderManager; import com.jme3.renderer.ViewPort; @@ -256,5 +257,10 @@ public class TestRenderToMemory extends SimpleApplication implements SceneProces public void cleanup() { } + @Override + public void setProfiler(AppProfiler profiler) { + + } + } diff --git a/jme3-examples/src/main/java/jme3test/post/TestSSAO2.java b/jme3-examples/src/main/java/jme3test/post/TestSSAO2.java index 27d419a80..ec1ea2c5a 100644 --- a/jme3-examples/src/main/java/jme3test/post/TestSSAO2.java +++ b/jme3-examples/src/main/java/jme3test/post/TestSSAO2.java @@ -36,11 +36,10 @@ import com.jme3.light.*; import com.jme3.material.Material; import com.jme3.math.*; import com.jme3.post.FilterPostProcessor; +import com.jme3.post.DetailedProfilerState; import com.jme3.post.ssao.SSAOFilter; import com.jme3.scene.*; -import com.jme3.scene.control.LodControl; import com.jme3.scene.shape.Box; -import com.jme3.texture.Texture; public class TestSSAO2 extends SimpleApplication { @@ -57,6 +56,11 @@ public class TestSSAO2 extends SimpleApplication { dl.setDirection(new Vector3f(-1,-1,-1).normalizeLocal()); rootNode.addLight(dl); + flyCam.setDragToRotate(true); + setPauseOnLostFocus(false); + + getStateManager().attach(new DetailedProfilerState()); + Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); mat.setFloat("Shininess", 16f); //mat.setBoolean("VertexLighting", true); @@ -96,7 +100,12 @@ public class TestSSAO2 extends SimpleApplication { FilterPostProcessor fpp = new FilterPostProcessor(assetManager); SSAOFilter ssaoFilter = new SSAOFilter(2.9299974f,25f,5.8100376f,0.091000035f); - ssaoFilter.setApproximateNormals(true); + int numSamples = context.getSettings().getSamples(); + if (numSamples > 0) { + fpp.setNumSamples(numSamples); + } + + //ssaoFilter.setApproximateNormals(true); fpp.addFilter(ssaoFilter); SSAOUI ui = new SSAOUI(inputManager, ssaoFilter); diff --git a/jme3-examples/src/main/java/jme3test/stress/TestShaderNodesStress.java b/jme3-examples/src/main/java/jme3test/stress/TestShaderNodesStress.java index 3364f5feb..dc74d3c0d 100644 --- a/jme3-examples/src/main/java/jme3test/stress/TestShaderNodesStress.java +++ b/jme3-examples/src/main/java/jme3test/stress/TestShaderNodesStress.java @@ -6,9 +6,7 @@ import com.jme3.material.Material; import com.jme3.math.ColorRGBA; import com.jme3.math.Quaternion; import com.jme3.math.Vector3f; -import com.jme3.profile.AppProfiler; -import com.jme3.profile.AppStep; -import com.jme3.profile.VpStep; +import com.jme3.profile.*; import com.jme3.renderer.ViewPort; import com.jme3.renderer.queue.RenderQueue; import com.jme3.scene.Geometry; @@ -98,5 +96,10 @@ public class TestShaderNodesStress extends SimpleApplication { } + @Override + public void spStep(SpStep step, String... additionalInfo) { + + } + } } diff --git a/jme3-ios/src/main/java/com/jme3/renderer/ios/IosGL.java b/jme3-ios/src/main/java/com/jme3/renderer/ios/IosGL.java index d73c5158a..95e8a213b 100644 --- a/jme3-ios/src/main/java/com/jme3/renderer/ios/IosGL.java +++ b/jme3-ios/src/main/java/com/jme3/renderer/ios/IosGL.java @@ -124,6 +124,11 @@ public class IosGL implements GL, GLExt, GLFbo { JmeIosGLES.glAttachShader(program, shader); } + @Override + public void glBeginQuery(int target, int query) { + throw new UnsupportedOperationException("Today is not a good day for this"); + } + public void glBindBuffer(int target, int buffer) { JmeIosGLES.glBindBuffer(target, buffer); } @@ -269,6 +274,11 @@ public class IosGL implements GL, GLExt, GLFbo { JmeIosGLES.glEnableVertexAttribArray(index); } + @Override + public void glEndQuery(int target) { + throw new UnsupportedOperationException("Today is not a good day for this"); + } + public void glGenBuffers(IntBuffer buffers) { checkLimit(buffers); JmeIosGLES.glGenBuffers(buffers.remaining(), temp_array, 0); @@ -281,6 +291,11 @@ public class IosGL implements GL, GLExt, GLFbo { fromArray(textures.remaining(), temp_array, textures); } + @Override + public void glGenQueries(int num, IntBuffer buff) { + throw new UnsupportedOperationException("Today is not a good day for this"); + } + public int glGetAttribLocation(int program, String name) { return JmeIosGLES.glGetAttribLocation(program, name); } @@ -311,6 +326,16 @@ public class IosGL implements GL, GLExt, GLFbo { return JmeIosGLES.glGetProgramInfoLog(program); } + @Override + public long glGetQueryObjectui64(int query, int pname) { + throw new UnsupportedOperationException("Today is not a good day for this"); + } + + @Override + public int glGetQueryObjectiv(int query, int pname) { + throw new UnsupportedOperationException("Today is not a good day for this"); + } + public void glGetShader(int shader, int pname, IntBuffer params) { checkLimit(params); JmeIosGLES.glGetShaderiv(shader, pname, temp_array, 0); diff --git a/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglGL.java b/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglGL.java index bbd1b24f9..e927b4bb7 100644 --- a/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglGL.java +++ b/jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglGL.java @@ -5,12 +5,7 @@ import com.jme3.renderer.opengl.GL; import com.jme3.renderer.opengl.GL2; import com.jme3.renderer.opengl.GL3; -import java.nio.Buffer; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; -import java.nio.IntBuffer; -import java.nio.ShortBuffer; +import java.nio.*; import com.jme3.renderer.opengl.GL4; import com.jogamp.opengl.GLContext; @@ -69,7 +64,12 @@ public class JoglGL implements GL, GL2, GL3, GL4 { } @Override - public void glBindBuffer(int param1, int param2) { + public void glBeginQuery(int target, int query) { + GLContext.getCurrentGL().getGL2ES2().glBeginQuery(target, query); + } + + @Override + public void glBindBuffer(int param1, int param2) { GLContext.getCurrentGL().glBindBuffer(param1, param2); } @@ -271,13 +271,23 @@ public class JoglGL implements GL, GL2, GL3, GL4 { } @Override - public void glGenBuffers(IntBuffer param1) { + public void glEndQuery(int target) { + GLContext.getCurrentGL().getGL2ES2().glEndQuery(target); + } + + @Override + public void glGenBuffers(IntBuffer param1) { checkLimit(param1); GLContext.getCurrentGL().glGenBuffers(param1.limit(), param1); } @Override - public void glGenTextures(IntBuffer param1) { + public void glGenQueries(int num, IntBuffer buff) { + GLContext.getCurrentGL().getGL2ES2().glGenQueries(num, buff); + } + + @Override + public void glGenTextures(IntBuffer param1) { checkLimit(param1); GLContext.getCurrentGL().glGenTextures(param1.limit(), param1); } @@ -569,6 +579,20 @@ public class JoglGL implements GL, GL2, GL3, GL4 { return new String(bytes); } + @Override + public long glGetQueryObjectui64(int query, int target) { + LongBuffer buff = LongBuffer.allocate(1); + GLContext.getCurrentGL().getGL2ES2().glGetQueryObjectui64v(query, target, buff); + return buff.get(0); + } + + @Override + public int glGetQueryObjectiv(int query, int pname) { + IntBuffer buff = IntBuffer.allocate(1); + GLContext.getCurrentGL().getGL2ES2().glGetQueryObjectiv(query, pname, buff); + return buff.get(0); + } + @Override public String glGetShaderInfoLog(int shader, int maxSize) { ByteBuffer buffer = ByteBuffer.allocateDirect(maxSize); diff --git a/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java b/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java index 59e50e3f7..c2694988c 100644 --- a/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java +++ b/jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java @@ -11,9 +11,12 @@ import java.nio.IntBuffer; import java.nio.ShortBuffer; import com.jme3.renderer.opengl.GL4; +import com.jme3.util.BufferUtils; import org.lwjgl.opengl.*; public final class LwjglGL implements GL, GL2, GL3, GL4 { + + IntBuffer tmpBuff = BufferUtils.createIntBuffer(1); private static void checkLimit(Buffer buffer) { if (buffer == null) { @@ -42,6 +45,11 @@ public final class LwjglGL implements GL, GL2, GL3, GL4 { GL20.glAttachShader(param1, param2); } + @Override + public void glBeginQuery(int target, int query) { + GL15.glBeginQuery(target, query); + } + public void glBindBuffer(int param1, int param2) { GL15.glBindBuffer(param1, param2); } @@ -206,11 +214,21 @@ public final class LwjglGL implements GL, GL2, GL3, GL4 { GL20.glEnableVertexAttribArray(param1); } + @Override + public void glEndQuery(int target) { + GL15.glEndQuery(target); + } + public void glGenBuffers(IntBuffer param1) { checkLimit(param1); GL15.glGenBuffers(param1); } + @Override + public void glGenQueries(int num, IntBuffer ids) { + GL15.glGenQueries(ids); + } + public void glGenTextures(IntBuffer param1) { checkLimit(param1); GL11.glGenTextures(param1); @@ -435,6 +453,16 @@ public final class LwjglGL implements GL, GL2, GL3, GL4 { return GL20.glGetProgramInfoLog(program, maxSize); } + @Override + public long glGetQueryObjectui64(int query, int target) { + return ARBTimerQuery.glGetQueryObjectui64(query, target); + } + + @Override + public int glGetQueryObjectiv(int query, int pname) { + return GL15.glGetQueryObjecti(query, pname); + } + public String glGetShaderInfoLog(int shader, int maxSize) { return GL20.glGetShaderInfoLog(shader, maxSize); } diff --git a/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java b/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java index f5f6a7b1a..a905c9f01 100644 --- a/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java +++ b/jme3-lwjgl3/src/main/java/com/jme3/renderer/lwjgl/LwjglGL.java @@ -71,6 +71,11 @@ public class LwjglGL implements GL, GL2, GL3, GL4 { GL20.glAttachShader(param1, param2); } + @Override + public void glBeginQuery(int target, int query) { + GL15.glBeginQuery(target, query); + } + public void glBindBuffer(int param1, int param2) { GL15.glBindBuffer(param1, param2); } @@ -235,6 +240,11 @@ public class LwjglGL implements GL, GL2, GL3, GL4 { GL20.glEnableVertexAttribArray(param1); } + @Override + public void glEndQuery(int target) { + GL15.glEndQuery(target); + } + public void glGenBuffers(IntBuffer param1) { checkLimit(param1); GL15.glGenBuffers(param1); @@ -245,6 +255,11 @@ public class LwjglGL implements GL, GL2, GL3, GL4 { GL11.glGenTextures(param1); } + @Override + public void glGenQueries(int num, IntBuffer ids) { + GL15.glGenQueries(ids); + } + public void glGetBoolean(int param1, ByteBuffer param2) { checkLimit(param2); GL11.glGetBooleanv(param1, param2); @@ -464,6 +479,16 @@ public class LwjglGL implements GL, GL2, GL3, GL4 { return GL20.glGetProgramInfoLog(program, maxSize); } + @Override + public long glGetQueryObjectui64(int query, int target) { + return ARBTimerQuery.glGetQueryObjectui64(query, target); + } + + @Override + public int glGetQueryObjectiv(int query, int pname) { + return GL15.glGetQueryObjecti(query, pname); + } + public String glGetShaderInfoLog(int shader, int maxSize) { return GL20.glGetShaderInfoLog(shader, maxSize); } diff --git a/jme3-niftygui/src/main/java/com/jme3/niftygui/NiftyJmeDisplay.java b/jme3-niftygui/src/main/java/com/jme3/niftygui/NiftyJmeDisplay.java index 53967f4af..4f6d6de7b 100644 --- a/jme3-niftygui/src/main/java/com/jme3/niftygui/NiftyJmeDisplay.java +++ b/jme3-niftygui/src/main/java/com/jme3/niftygui/NiftyJmeDisplay.java @@ -42,6 +42,7 @@ import com.jme3.audio.AudioRenderer; import com.jme3.input.InputManager; import com.jme3.input.event.KeyInputEvent; import com.jme3.post.SceneProcessor; +import com.jme3.profile.AppProfiler; import com.jme3.renderer.RenderManager; import com.jme3.renderer.Renderer; import com.jme3.renderer.ViewPort; @@ -71,6 +72,7 @@ public class NiftyJmeDisplay implements SceneProcessor { protected ResourceLocationJme resourceLocation; protected int w, h; + private AppProfiler prof; protected class ResourceLocationJme implements ResourceLocation { @@ -359,4 +361,9 @@ public class NiftyJmeDisplay implements SceneProcessor { } } + @Override + public void setProfiler(AppProfiler profiler) { + this.prof = profiler; + } + }