-
Notifications
You must be signed in to change notification settings - Fork 3.5k
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
Improve documentation and readability in Scene and depth-related code #12188
Conversation
Thank you for the pull request, @jjhembd! ✅ We can confirm we have a CLA on file for you. |
@javagl Would love to get your input on this. Also a general comment that it would be helpful to prioritize getting this PR merged quickly to avoid merge conflicts as it touches core parts of the codebase. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First, a big 👍: I'm strongly convinced that there should be (and have been) more cleanup passes like this. Otherwise, the effect is commonly referred to as https://en.wikipedia.org/wiki/Lava_flow_(programming) . People are afraid of touching code (that it hard to read and fully understand), out of the fear of breaking things. And whatever they are going to do instead: It can not improve the situation. It can only make it worse. (Now iterate that...)
In terms of the review, a general disclaimer: When ~"code that needs to be cleaned up" eventually is cleaned up, it can be really hard to look at the change alone and be 100% sure that it does not introduce changes in the behavior (including bugs). But multiple reviews and tests should make "serious" bugs less likely.
I did look over the changes via the commits, the "Files changed" as a whole, and for some of them, at the new state only (locally in VSCode).
- Some commits seem to only address the "Declare variables where they are first used." guideline. That's good. These nearly-global variables also bugged me. The
let
always raises the questions: Will it change? And where? And how? We might even consider allocating some time/cycles to just do this, mechanically, as part of maintenance (orchestrated properly, to avoid merge conflicts) - Other commits did further structural cleanups. Some of them may be subjective.
- I'm usually not using the
const { destructuring, assignment } = syntax;
, but ... it's concise and clean, so hard to argue against. - I also prefer "early returns" when they are used as "watchdogs" at the beginning of a function, and decrease the indentation level, and have seen that this was done in a few places
- Pulling out
repetitive.parts.of.property.accesses
seems uncontoversial - Similarly, pulling out common blocks into functions that have names and documentation
- I'm usually not using the
- I've been staring at ad274f8 for quite a while. And I hesitate to ask 😆...
There are some minor inlined comments, but nothing critical, I guess.
I also compiled and started that state locally, and tried out some of the Sandcastles. (I did not try all of them, and all possible interactions, but ... focussed on some of the "rendering-heavy" ones with sophisticated features, like Depth-Of-Field and whatnot). I did not notice any issues.
(I also ran the tests locally, and some of them did fail, but I'm currently having some network connection issues, so that doesn't mean anything for this PR...)
Unrelated to this PR, but maybe as sorts of follow-ups:
- The Coding Guide does not say anyhing about "destructuring assignment". Is this preferred or discouraged, or does it not matter?
- One of the more obscure abominations of what is considered to be "JavaScript code" is that
for (const key in obj) { if (!obj.hasOwnProperty(key)) { ... } }
. Should the Coding Guide recommendfor (const key of Object.keys(obj))
instead? (I'd say: YES!) - Some commits removed unused parameters. Maybe this should be checked via linting? (VSCode points it out, at least...)
@javagl I think I addressed all your feedback. I also ran the end-to-end tests, comparing all the Sandcastle screenshots against the current main branch. No problems were detected. |
@javagl for your more general comments,
|
It may be a detail, but ... you know, I'm a strickler. To be clear: Unless I'm overlooking something obvious, the code change in this commit should not have an effect. The behavior should be the same in both cases. Everybody could (and should be able to!) throw in such a change into a PR, as part of a cleanup, and it should not cause randomly and inexplicably cause the build to break. Do you happen to know (whether the test failure was really related to that, and not just a random fluke, and) what exactly the failure was? (It may not be worth spending additional time for that, but it caused quite a 🤨 for me...)
I guess that this is somewhat related to the concept of an "interface" that does not exist on the same level as it does in a proper(ly) typed language. There probably are many places where parameters are passed in, just to match an "interface". There probably are solutions for that. (Maybe something like letting the parameter name start with an Beyond the scope of (and somewhat unrealated to) this PR, but a few short notes:
The pattern of In terms of alternatives (or rather: possible improvements), there are many things to consider. Just quick bullet points:
One important question are modifications. I just did a quick test. The behavior is not tooo surprising, but ... subtle. The
Now, breaking that down to machine code and examining the performance- and memory implications for objects with 10, 1000, 100000 properties is a different story, but I was curious about the basics. You never know... function Example() {
this.propertyA = "A";
this.propertyB = true;
this.propertyC = 123;
}
function runModifyA(example) {
for (const key in example) {
if (example.hasOwnProperty(key)) {
const value = example[key];
console.log(value);
if (value === true) {
example.propertyC = "CHANGED_VALUE";
example.propertyD = "ADDED_VALUE";
}
}
}
}
function runModifyB(example) {
for (const key of Object.keys(example)) {
const value = example[key];
console.log(value);
if (value === true) {
example.propertyC = "CHANGED_VALUE";
example.propertyD = "ADDED_VALUE";
}
}
}
function runModifyC(example) {
for (const value of Object.values(example)) {
console.log(value);
if (value === true) {
example.propertyC = "CHANGED_VALUE";
example.propertyD = "ADDED_VALUE";
}
}
}
function print(example) {
for (const value of Object.values(example)) {
console.log(value);
}
}
const exampleA = new Example();
const exampleB = new Example();
const exampleC = new Example();
console.log("runModifyA");
runModifyA(exampleA);
console.log("\n");
console.log("result:");
print(exampleA);
console.log("\n");
console.log("\n");
console.log("runModifyB");
runModifyB(exampleB);
console.log("\n");
console.log("result:");
print(exampleB);
console.log("\n");
console.log("\n");
console.log("runModifyC");
runModifyC(exampleC);
console.log("\n");
console.log("result:");
print(exampleC);
console.log("\n");
console.log("\n"); |
Here is the spec that breaks when that last 1) disables frustum culling
Scene/Model/Model frustum culling
TypeError: Cannot read properties of undefined (reading 'far')
at View.createPotentiallyVisibleSet (packages/engine/Source/Scene/View.js:440:69 <- Build/CesiumUnminified/Cesium.js:213675:72) For completeness, here is the change I make to get that error. In for (let j = 0; j < numFrustums; ++j) {
frustumSplits[j] = frustumCommandsList[j].near;
//if (j === numFrustums - 1) {
// frustumSplits[j + 1] = frustumCommandsList[j].far;
//}
}
frustumSplits[numFrustums] = frustumCommandsList[numFrustums - 1].far; The failure (and the fix) have been 100% repeatable for me over many runs, so I don't think it's a random fluke. Your |
The reason for why that commit made sense was (of course, embarrassingly obvious in hindsight): For the case of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few comments. Many are not necessary to merge the PR, but I figured I'd point them out. :)
function executeCommand(command, scene, context, passState) { | ||
command.execute(context, passState); | ||
function executeCommand(command, scene, passState) { | ||
command.execute(scene._context, passState); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is external to the class, let's use the getter: scene.context
.
|
||
//>>includeStart('debug', pragmas.debug); | ||
if (frustum.fov < 0 || frustum.fov >= Math.PI) { | ||
throw new DeveloperError("fov must be in the range [0, PI)."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No changes strictly needed for the sake of this PR, but keep in mind that Check
has some functions to help with number ranges like this.
|
||
if (!writesDepthOrDiscards && !usesLogDepth) { | ||
const source = | ||
"void main() \n" + "{ \n" + " out_FragColor = vec4(1.0); \n" + "} \n"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, while strictly not needed for this PR, keep in mind that template strings can help with multiline blocks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, some of this was a little messy. I went ahead and updated to template strings in this file.
const hasLogDepthDerivedCommands = defined(derivedCommands.logDepth); | ||
const hasHdrCommands = defined(derivedCommands.hdr); | ||
const hasDerivedCommands = defined(derivedCommands.originalCommand); | ||
const needsLogDepthDerivedCommands = | ||
useLogDepth && !hasLogDepthDerivedCommands; | ||
const needsHdrCommands = useHdr && !hasHdrCommands; | ||
// PERFORMANCE_IDEA: if (!useLogDepth && !useHdr), we may be able to save a call to updateDerivedCommands. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This may be up for debate, but I'm generally not a fan of leaving TODO comments like this in the code. It's too easy to forget about them and then they never get implemented. Do you think this will indeed be useful for a future developer?
/** | ||
* Get the central body occluder for the scene. | ||
* | ||
* TODO: The occluder is the top-level globe. When we add |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar comment about TODO comments in the code. Would this be more helpful if it lived in an issue instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case, I couldn't find any discussion about "support for multiple central bodies," so I don't think an issue would be appropriate.
I changed the comment to simply describe the assumptions made by this method, so that if someone does want to discuss multiple bodies, they will understand what to change.
* based on its bounding volume. | ||
* | ||
* @param {DrawCommand} [command] The draw command | ||
* @param {CullingVolume} cullingVolume The culling volume of the current Scene. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The order of "optional, required, optional" parameters is non-ideal. Again, not strictly required for this PR, but it would be best if we ordered the signature with 'required, optional, optional".
@ggetz thanks for the review! I think I addressed all your comments. |
Great, thanks @jjhembd! To merge in main, you'll want to follow @jjspace's instructions to make things less of a headache. |
The last commit before Prettier v3 was used to reformat the code
This commit reformats all code using prettier v3 relative to the pre-prettier-v3 tag
@ggetz merge is done! |
Looks good! Thanks @jjhembd! |
Description
This PR is a first step towards improved maintainability in some of the core rendering code, especially code that touches depth buffers. The focus is on classes like
Scene
,View
,GlobeDepth
,Framebuffer
, andDerivedCommand
, with minor documentation updates in connected classes.Main changes include:
executeCommands
inScene
into smaller functions, borrowing ideas (and some code!) from Break executeCommands into functions #12058Issue number and link
Testing plan
Author checklist
CONTRIBUTORS.md
CHANGES.md
with a short summary of my change