Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix slow debug inspector #9004

Merged
merged 14 commits into from
Jul 9, 2020
Merged
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 136 additions & 0 deletions Source/Scene/DebugInspector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import clone from "../Core/clone.js";
import Color from "../Core/Color.js";
import DrawCommand from "../Renderer/DrawCommand.js";
import ShaderSource from "../Renderer/ShaderSource.js";
import ShaderProgram from "../Renderer/ShaderProgram.js";
import defined from "../Core/defined.js";

/**
* @private
*/
function DebugInspector() {
this._cachedShowFrustumsShaders = {};
}

function getAttributeLocations(shaderProgram) {
var attributeLocations = {};
var attributes = shaderProgram.vertexAttributes;
for (var a in attributes) {
if (attributes.hasOwnProperty(a)) {
attributeLocations[a] = attributes[a].index;
}
}

return attributeLocations;
}

function createDebugShowFrustumsShaderProgram(scene, shaderProgram) {
var context = scene.context;
var sp = shaderProgram;
var fs = sp.fragmentShaderSource.clone();

var targets = [];
fs.sources = fs.sources.map(function (source) {
source = ShaderSource.replaceMain(source, "czm_Debug_main");
var re = /gl_FragData\[(\d+)\]/g;
var match;
while ((match = re.exec(source)) !== null) {
if (targets.indexOf(match[1]) === -1) {
targets.push(match[1]);
}
}
return source;
});
var length = targets.length;

var newMain = "";
newMain += "uniform vec3 debugShowCommandsColor;\n";
newMain += "uniform vec3 debugShowFrustumsColor;\n";
newMain += "void main() \n" + "{ \n" + " czm_Debug_main(); \n";

// set debugShowCommandsColor to Color(1.0, 1.0, 1.0, 1.0) to stop rendering scene.debugShowCommands
// set debugShowFrustumsColor to Color(1.0, 1.0, 1.0, 1.0) to stop rendering scene.debugShowFrustums
var i;
if (length > 0) {
for (i = 0; i < length; ++i) {
newMain +=
" gl_FragData[" + targets[i] + "].rgb *= debugShowCommandsColor;\n";
newMain +=
" gl_FragData[" + targets[i] + "].rgb *= debugShowFrustumsColor;\n";
}
} else {
newMain += " gl_FragColor.rgb *= debugShowCommandsColor;\n";
newMain += " gl_FragColor.rgb *= debugShowFrustumsColor;\n";
}
newMain += "}";

fs.sources.push(newMain);

var attributeLocations = getAttributeLocations(sp);

return ShaderProgram.fromCache({
context: context,
vertexShaderSource: sp.vertexShaderSource,
fragmentShaderSource: fs,
attributeLocations: attributeLocations,
});
}

DebugInspector.prototype.createShowFrustumsCommand = function (scene, command) {
// create debug command
var debugCommand = DrawCommand.shallowClone(command);
var shaderProgramId = command.shaderProgram.id;
if (!defined(this._cachedShowFrustumsShaders[shaderProgramId])) {
debugCommand.shaderProgram = createDebugShowFrustumsShaderProgram(
scene,
command.shaderProgram
);

this._cachedShowFrustumsShaders[shaderProgramId] =
debugCommand.shaderProgram;
} else {
debugCommand.shaderProgram = this._cachedShowFrustumsShaders[
shaderProgramId
];
}

// setup uniform for the shader
if (!defined(command.uniformMap) || typeof command.uniformMap !== "object") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When is the uniform map defined but not an object?

debugCommand.uniformMap = {};
} else {
debugCommand.uniformMap = clone(command.uniformMap);
}

if (scene.debugShowCommands) {
if (!defined(command._debugColor)) {
command._debugColor = Color.fromRandom();
}

debugCommand.uniformMap.debugShowCommandsColor = function () {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably okay since this is debug code, but normally we wouldn't set the uniform function every frame since it allocates a new function object, creates a closure, etc, all of which can add garbage collection pressure. Instead we would add the uniform function on the command once. Maybe you could add debugShowCommandsColor only if the uniform map doesn't already have a uniform with that name.

return command._debugColor;
};
} else {
debugCommand.uniformMap.debugShowCommandsColor = function () {
return new Color(1.0, 1.0, 1.0, 1.0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More concise, and avoids the allocation when retrieving the uniform value every frame.

Suggested change
return new Color(1.0, 1.0, 1.0, 1.0);
return Color.WHITE;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment for debugShowFrustumsColor

};
}

if (scene.debugShowFrustums) {
// Support up to three frustums. If a command overlaps all
// three, it's code is not changed.
var r = command.debugOverlappingFrustums & (1 << 0) ? 1.0 : 0.0;
var g = command.debugOverlappingFrustums & (1 << 1) ? 1.0 : 0.0;
var b = command.debugOverlappingFrustums & (1 << 2) ? 1.0 : 0.0;

debugCommand.uniformMap.debugShowFrustumsColor = function () {
return new Color(r, g, b, 1.0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar comment to above. If you end up making a similar change here be sure use command in the closure instead of r, g, b since debugOverlappingFrustums changes every frame. E.g.

if (!defined(debugCommand.uniformMap.debugShowFrustumsColor)) {
    debugCommand.uniformMap.debugShowFrustumsColor = function () {
        var r = command.debugOverlappingFrustums & (1 << 0) ? 1.0 : 0.0;
        var g = command.debugOverlappingFrustums & (1 << 1) ? 1.0 : 0.0;
        var b = command.debugOverlappingFrustums & (1 << 2) ? 1.0 : 0.0;
        return new Color(r, g, b, 1.0);
    }
}

Also instead of new Color(r, g, b, 1.0); you could use a scratch color to avoid the allocation. The GL uniforms get updated immediately after the function call so scratches are safe to use.

};
} else {
debugCommand.uniformMap.debugShowFrustumsColor = function () {
return new Color(1.0, 1.0, 1.0, 1.0);
};
}

return debugCommand;
};
export default DebugInspector;
131 changes: 7 additions & 124 deletions Source/Scene/Scene.js
Original file line number Diff line number Diff line change
@@ -34,11 +34,8 @@ import ClearCommand from "../Renderer/ClearCommand.js";
import ComputeEngine from "../Renderer/ComputeEngine.js";
import Context from "../Renderer/Context.js";
import ContextLimits from "../Renderer/ContextLimits.js";
import DrawCommand from "../Renderer/DrawCommand.js";
import Pass from "../Renderer/Pass.js";
import RenderState from "../Renderer/RenderState.js";
import ShaderProgram from "../Renderer/ShaderProgram.js";
import ShaderSource from "../Renderer/ShaderSource.js";
import BrdfLutGenerator from "./BrdfLutGenerator.js";
import Camera from "./Camera.js";
import Cesium3DTilePass from "./Cesium3DTilePass.js";
@@ -72,6 +69,7 @@ import SunLight from "./SunLight.js";
import SunPostProcess from "./SunPostProcess.js";
import TweenCollection from "./TweenCollection.js";
import View from "./View.js";
import DebugInspector from "./DebugInspector.js";

var requestRenderAfterFrame = function (scene) {
return function () {
@@ -262,6 +260,7 @@ function Scene(options) {
this._postRender = new Event();

this._minimumDisableDepthTestDistance = 0.0;
this._debugInspector = new DebugInspector();

/**
* Exceptions occurring in <code>render</code> are always caught in order to raise the
@@ -1964,126 +1963,6 @@ Scene.prototype.isVisible = function (command, cullingVolume, occluder) {
);
};

function getAttributeLocations(shaderProgram) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great idea to move this into it's own file and make Scene a little less cluttered

var attributeLocations = {};
var attributes = shaderProgram.vertexAttributes;
for (var a in attributes) {
if (attributes.hasOwnProperty(a)) {
attributeLocations[a] = attributes[a].index;
}
}

return attributeLocations;
}

function createDebugFragmentShaderProgram(command, scene, shaderProgram) {
var context = scene.context;
var sp = defaultValue(shaderProgram, command.shaderProgram);
var fs = sp.fragmentShaderSource.clone();

var targets = [];
fs.sources = fs.sources.map(function (source) {
source = ShaderSource.replaceMain(source, "czm_Debug_main");
var re = /gl_FragData\[(\d+)\]/g;
var match;
while ((match = re.exec(source)) !== null) {
if (targets.indexOf(match[1]) === -1) {
targets.push(match[1]);
}
}
return source;
});
var length = targets.length;

var newMain = "void main() \n" + "{ \n" + " czm_Debug_main(); \n";

var i;
if (scene.debugShowCommands) {
if (!defined(command._debugColor)) {
command._debugColor = Color.fromRandom();
}
var c = command._debugColor;
if (length > 0) {
for (i = 0; i < length; ++i) {
newMain +=
" gl_FragData[" +
targets[i] +
"].rgb *= vec3(" +
c.red +
", " +
c.green +
", " +
c.blue +
"); \n";
}
} else {
newMain +=
" " +
"gl_FragColor" +
".rgb *= vec3(" +
c.red +
", " +
c.green +
", " +
c.blue +
"); \n";
}
}

if (scene.debugShowFrustums) {
// Support up to three frustums. If a command overlaps all
// three, it's code is not changed.
var r = command.debugOverlappingFrustums & (1 << 0) ? "1.0" : "0.0";
var g = command.debugOverlappingFrustums & (1 << 1) ? "1.0" : "0.0";
var b = command.debugOverlappingFrustums & (1 << 2) ? "1.0" : "0.0";
if (length > 0) {
for (i = 0; i < length; ++i) {
newMain +=
" gl_FragData[" +
targets[i] +
"].rgb *= vec3(" +
r +
", " +
g +
", " +
b +
"); \n";
}
} else {
newMain +=
" " +
"gl_FragColor" +
".rgb *= vec3(" +
r +
", " +
g +
", " +
b +
"); \n";
}
}

newMain += "}";

fs.sources.push(newMain);

var attributeLocations = getAttributeLocations(sp);

return ShaderProgram.fromCache({
context: context,
vertexShaderSource: sp.vertexShaderSource,
fragmentShaderSource: fs,
attributeLocations: attributeLocations,
});
}

function executeDebugCommand(command, scene, passState) {
var debugCommand = DrawCommand.shallowClone(command);
debugCommand.shaderProgram = createDebugFragmentShaderProgram(command, scene);
debugCommand.execute(scene.context, passState);
debugCommand.shaderProgram.destroy();
}

var transformFrom2D = new Matrix4(
0.0,
0.0,
@@ -2260,7 +2139,11 @@ function executeCommand(command, scene, context, passState, debugFramebuffer) {
}

if (scene.debugShowCommands || scene.debugShowFrustums) {
executeDebugCommand(command, scene, passState);
var debugCommand = scene._debugInspector.createShowFrustumsCommand(
scene,
command
);
debugCommand.execute(scene.context, passState);
return;
}