Fix memory leak from WebGLState light/shadow data from texture generation#227
Fix memory leak from WebGLState light/shadow data from texture generation#227
Conversation
| import '@polymer/iron-demo-helpers/demo-snippet.js'; | ||
| import '@polymer/paper-button/paper-button.js'; | ||
| import '@polymer/paper-slider/paper-slider.js'; | ||
| import '@polymer/paper-radio-group/paper-radio-group.js'; |
There was a problem hiding this comment.
I like using these nice components in our demos, but maybe these should be broken out per dependency as-needed in demos (e.g. only the PBR demo and fuzzer use paper-button)
There was a problem hiding this comment.
There should a world where we have a build tool that figures out all of the bundle fragments across all examples without manual configuration and builds those for us (Polymer CLI does static analysis and supports configuration to do just this).
Filed #229 to track
examples/fuzzer.html
Outdated
|
|
||
| updated.style.transition = 'none'; | ||
| updated.style.opacity = 1; | ||
| requestAnimationFrame(() => { |
There was a problem hiding this comment.
This "update" should flash and fade on every change; doesn't work too well for lower values. Could remove
There was a problem hiding this comment.
What is the value of the fade? Just ensuring that partial opacity doesn't produce unexpected side-effects?
There was a problem hiding this comment.
Just to indicate to the viewer that a change occurred (more interesting for longer wait times), since sometimes the attribute changes do not result in a visible change
There was a problem hiding this comment.
Going to remove this for now
| input: './lib/model-viewer.js', | ||
| output: { | ||
| file: './dist/model-viewer.js', | ||
| sourcemap: true, |
There was a problem hiding this comment.
Needed for debugging memory leaks, can remove and open another issue (valuable but we're building a lot of things per npm run build currently)
There was a problem hiding this comment.
WFM our users would probably appreciate the source maps being published w/ the package anyway.
src/three-components/TextureUtils.js
Outdated
| * @return {THREE.Texture} | ||
| */ | ||
| equirectangularToCubemap(texture) { | ||
| // Each instance of EquirectangularToCubeGenerator creates a new camera, |
There was a problem hiding this comment.
This special handling could go away if we make upstream fixes in three's generator
There was a problem hiding this comment.
Is there an open issue against Three? Maybe link it in the comment with a note about when we can take this out?
726bf87 to
69c257a
Compare
This scenario seems like it would be more common in a WYSIWYG tool that enabled GUI-based configuration of the |
cdata
left a comment
There was a problem hiding this comment.
Looks great, some minor changes requested
| import '@polymer/iron-demo-helpers/demo-snippet.js'; | ||
| import '@polymer/paper-button/paper-button.js'; | ||
| import '@polymer/paper-slider/paper-slider.js'; | ||
| import '@polymer/paper-radio-group/paper-radio-group.js'; |
There was a problem hiding this comment.
There should a world where we have a build tool that figures out all of the bundle fragments across all examples without manual configuration and builds those for us (Polymer CLI does static analysis and supports configuration to do just this).
Filed #229 to track
| input: './lib/model-viewer.js', | ||
| output: { | ||
| file: './dist/model-viewer.js', | ||
| sourcemap: true, |
There was a problem hiding this comment.
WFM our users would probably appreciate the source maps being published w/ the package anyway.
examples/fuzzer.html
Outdated
|
|
||
| updated.style.transition = 'none'; | ||
| updated.style.opacity = 1; | ||
| requestAnimationFrame(() => { |
There was a problem hiding this comment.
What is the value of the fade? Just ensuring that partial opacity doesn't produce unexpected side-effects?
| }]; | ||
| attributes.forEach(attr => attr.values.push(null, '')); | ||
|
|
||
| function updateAttribute (attr, values) { |
There was a problem hiding this comment.
I don't know how to do this in a way that won't have potentially non-relevant memory/perf side-effects, but it would helpful to log what changes happened over time somehow.
There was a problem hiding this comment.
(No action required, just observing)
There was a problem hiding this comment.
I was thinking of a rolling log of the last 10 changes or so when stopping, to debug a scenario where going from state X to Y causes visual artifact Z
|
|
||
| import * as THREE from 'three'; | ||
|
|
||
| const EquirectangularToCubeGenerator = function ( sourceTexture, options ) { |
There was a problem hiding this comment.
Is this modified from the Three version? If so, can you call out the modifications?
There was a problem hiding this comment.
Not other than the ES6 wrapper stuff, no
|
|
||
| // Enable three's loader cache so we don't create redundant | ||
| // Image objects to decode images fetched over the network. | ||
| Cache.enabled = true; |
| this.config = {...defaultConfig, ...config}; | ||
| this.renderer = renderer; | ||
| this.cubemapGenerator = new EquirectangularToCubemap(this.renderer); | ||
| this.envMapGenerator = new EnvMapGenerator(this.renderer); |
There was a problem hiding this comment.
Nit: initialize class field $cubeGenerator
src/three-components/TextureUtils.js
Outdated
| * @return {THREE.Texture} | ||
| */ | ||
| equirectangularToCubemap(texture) { | ||
| // Each instance of EquirectangularToCubeGenerator creates a new camera, |
There was a problem hiding this comment.
Is there an open issue against Three? Maybe link it in the comment with a note about when we can take this out?
src/three-components/TextureUtils.js
Outdated
| // the prototype since this is called in the constructor. | ||
| if (!this[$cubeGenerator]) { | ||
| const material = generator.boxMesh.material; | ||
| EquirectangularToCubeGenerator.prototype.getShader = () => material; |
There was a problem hiding this comment.
Is it possible to just create an extended class rather than monkey patching on the fly here? e.g.,
class NonLeakyEquirectangularToCubeGenerator extends EquirectangularToCubeGenerator {
constructor(sharedShaderMaterial = null) {
super();
this.sharedShaderMaterial = sharedShaderMaterial;
}
getShader() {
if (this.sharedShaderMaterial != null) {
return this.sharedShaderMaterial;
}
return super.getShader();
}
}There was a problem hiding this comment.
Good call, trying this
69c257a to
5d0b6e5
Compare
scene/camera for every texture. Add fuzzer.html example to stress test memory leaks, race conditions and uncommon attribute combinations. Switch to using three's EquirectangularToCubeGenerator, and extend with a patched version until an upstream fix in three. Fixes #220.
5d0b6e5 to
f995f77
Compare
cdata
left a comment
There was a problem hiding this comment.
LGTM 👍 excited about the potential of this fuzzer
With the background-image demo often open on my machine, after long periods of time, my machine would hang, and had a hunch it had to do with the demo's infinite loading of new textures. In terms of severity, this would only occur when constantly changing textures (not a realistic scenario IMO) over a long period of time as small amounts of resources were being generated and not collected.
The leak wasn't terribly severe tracked leak down to cubemap generator, figured might as well update
three.js creates a WebGLState for every camera/scene combo rendered to store light and shadow data, and the three.equirectangular-to-cubemap component we use currently creates a new camera for every texture -- resulting in a new WebGLState created and retained, and does not get disposed unless calling
disposeon the renderer, which will have other unintended side-effects in our case since we're not yet done with our renderer.As a part of this change, I figured it'd be a good time to update to three's EquirectangularToCubeGenerator so we can ensure this works with the version of three we're using as a dependency (Fixes #220). Unfortunately, this generator similarly creates a new scene and camera for every texture, and not designed to handle multiple texture conversions, and the workarounds to cache these values are documented in the code.
Will file a follow up with three with changes to EquirectangularToCubeGenerator so that it can handle this scenario without creating extraneous light/shadow data, which would simplify a lot of this code when we next upgrade three.
--
Also included is a fuzzer page that, at random intervals, flips some values on the model. The element handles this surprisingly well, but a great test bed to ensure unexpected attribute combinations work well, and shake out any race conditions we have. Using memory tools, we can leave this run for quite some time and see that the memory doesn't grow.