-
-
Notifications
You must be signed in to change notification settings - Fork 36.2k
WebGLTextures: Avoid unnecessary texture uploads. #17949
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
Conversation
|
Updated the PR by introducing I could also think of a more radical change: In such a scenario, |
|
@Mugen87 Thank you for working on this! You claim that your previous solution was a hack — do you see anything wrong with it other than pollution of the Image objects? I'm not opposed to adding a new TextureImage class, but if modifying Image properties was the only problem (I don't see any other issues) there may be an option that could be hidden from the public API: Instead of using uploadedTextureCache = _images.get( image );
uploadedTextureCache[ textureCacheKey ] = {
uuid: ...,
version: ...,
glTexture: ...
};
_images.set( image, uploadedTextureCache );All else being equal, it might be nice to hide this implementation detail from the public API. |
A new class would be the ideal place for the respective |
|
Could you express what makes it seem like a hack? BufferAttribute does not require a separate subclass to represent and serialize its |
Can you please explain why it's a breaking change? Since the
I just think the image-related code perfectly fits into |
|
Oops I see. Agreed, it's not a breaking change. It's still a bit complex; the meanings of
^Arguably the word "Image" could be replaced with "Texture" for any of those. Or the |
|
That sounds everything very reasonable 😊. Let's see what @mrdoob and the others prefer from these options. |
|
It think the name |
|
+1 to this! I encountered a similar problem when using GLTFLoader with KHR_texture_transform. Although there are other issues to be fixed there, having this PR would be crucial for a proper solution. |
|
Just noting that this is important to fix for KHR_mesh_quantization extension as well since it relies on KHR_texture_transform for providing dequantization data. |
|
More ideas on this subject... Instead of adding a new class under const texture = new THREE.TextureLoader().load( 'foo.png' );
const material = new THREE.MeshBasicMaterial();
material.map = new THREE.Sampler( texture );
material.map.offset.x = 0.5;One could still do Another name option would be |
|
Adding a class on top might also fit better with the node material pattern (like how |
|
Closing. The PR is full of merge conflicts and the |
|
@Mugen87 Would you like to give it a go at implementing |
|
Yes 😊 . Already thinking about it^^. It's actually more complex to introduce |
|
Seeing that the How about renaming We can then introduce a new |
|
Or, to keep things more open it could just be const image = new THREE.ImageLoader().load( 'foo.png' );
const source = new THREE.Source( image ); // source.data = image
const material = new THREE.MeshBasicMaterial();
material.map = new THREE.Texture( source );
material.map.offset.x = 0.5;Sorry for the back and forth 😅 |
|
Let me create a new PR with this approach. |
Fixed #17766
Fixed #5821
This is a draft PR that should demonstrate what kind of changes are necessary to avoid unnecessary texture uploads in
three.js.Background
three.jsis currently not able to detect shared images in textures. For example when you clone a texture multiple times,three.jsis going to create an internalWebGLTextureobject for each texture. And that means unnecessary processing and memory allocation.The Current Solution
I've tried to approach this problem in different ways. This PR represents a change with the least impact on the code base. However, it requires a hack in the
THREE.Texture. This hack clearly shows what kind of new logic is required to avoid multiple uploads.The idea is to add a
versionproperty toTexture.image. That makes it possible to determine whether a texture upload is required or not. Besides,WebGLTexturesonly creates raw WebGL texture objects if necessary now. This is done by introducing a new weak map that maps WebGL texture objects to images. The problem is that a single image can have multiple raw WebGL textures because of different texture properties. Some of these properties are mostly static and closely coupled to the actual texture data (likeformat,typeorencoding). Other parameters likewrapS/wrapTmight be different from texture to texture. To accommodate this issue,WebGLTexturesnow computes a key viagetTextureCacheKey()based on the texture properties and uses it to identify raw WebGL texture objects. It's similar to howthree.jsuses material parameters to reuse existing shader programs.In any event, the enhancement of
Textureis a hack and thus not a clean solution. However, introducingTHREE.Imagewill be a breaking change because the semantics ofTexture.imageis going to change.The Better Solution
I think I would rename
THREE.Imageto something likeTHREE.TextureImagein order to avoid name conflicts with the nativeImagector. The type ofTexture.imagewould be changed toTHREE.TextureImageor a new property introduced (so it's easier to ensure backwards compatibility). Both cases will require refactoring in loaders and serialization/deserialization logic however hacks like this won't be necessary anymore:three.js/src/textures/Texture.js
Line 186 in 7680fb5
Note that the same type of hack (pollute objects with custom properties) is used to assign uuids to images. Introducing
THREE.TextureImagewould avoid this by wrapping the image into a separate object. Something like:I'm trying to experiment with this approach in the next time to better understand the impact on the code base and if it's possible to introduce
TextureImagewithout breaking code. Fortunately, I can reuse code from this PR, especially the changes inWebGLTexturesmight be useful.