I have a custom AssetPostprocessor that I’m using to optimize a bunch of 2D spritework. I’m actually doing a lot of processing, but here is a simplified flow:
OnPreprocessTexture:
Store certain import settings and tell the importer to import at maximum resolution, uncompressed RGBA.
OnPostprocessTexture:
Restore the import settings
Crop the sprite
downscale the sprite to the desired maxres we stored earlier
pad out the sprite, if neccessary, so that both dimensions are a multiple of 4 (compression related)
etc etc
compress the texture to the format we stored earlier
No matter what I do, whenever I restore the original settings, the editor will indicate that the sprite is “not yet compressed”, as if it has some pending settings, even though I perform the compression manually from code. If I do not restore the settings (very user unfriendly) it works fine, and will indicate the right format/file size (that was set/manually compressed to from code). Regardless of whether I restore or not (and whether or not the editor thinks it still needs to be compressed), according to the memory profile it IS compressed to the format I want. It seems to be a case of an internal flag not being cleared when it probably should.
This behaviour is the case both with texture.Compress as well as EditorUtility.CompressTexture.
It seems to be caused by the TextureImporter.textureStillNeedsToBeCompressed flag, which I think is automatically set to true whenever the importer’s texture format setting is changed (and NOT subsequently set back to false like one would expect by compressing it manually from code). The flag itself is read-only, and according to the C# source binds directly to the C++ side (get only).
Nothing I do appears to clear this flag:
TextureImporter.SaveAndReimport
EditorUtility.SetDirty (Texture2D nor TextureImporter)
AssetDatabase.WriteImportSettingsIfDirty ( assetImporter.assetPath nor the manual paths of either the Texture2D or the TextureImporter )
AssetDatabase.SaveAssets
EditorUtility.ClearDirty ( Texture2D nor TextureImporter )
AssetDatabase.Refresh
Is there really no way to clear this flag from code? Design oversight from a system that wasn’t made with AssetPostprocessor in mind?
Hi @Higgenbothom !
Could you report a bug with your script?
I’ll get in touch with the team responsible for the TextureImporter to see if there is any workaround that would make it work, or if it’s actually a bug that should be fixed.
You can reply here with your bug number once you’ve submitted it so I can find it and track it easily.
@bastien_humeau you’ll be pleased to hear that the bugticket ID is IN-14840. The ticket links back to this thread.
(Oh hello there sir bugticket engineer, I hope you have a good day)
Hey @Higgenbothom , I had a quick look and I have some doubts with how it works today …
Let me start with the good news: as far as I can tell, everything is in fact going as you would expect. As you mentioned, it indeed looks like everything is properly compressed (regardless of that message saying it isn’t). So in that regard, I don’t think there’s any real blocker for you (apart from the annoyance of not getting your full texture info).
I’ll see what we can do to address the issue. If these observations are confirmed, this should be fixed quickly.
Now for some context:
That flag is not actually stored, it’s a property that’s dynamically determined when requested …
That flag is determined based on texture importer settings (I’d have to dig back 13 years to ask for details)
So in your case, it thinks that the texture was imported as uncompressed, and on the target it needs to be compressed so it mentions this.
Luckily this is only messaging; the actual behavior appears to be correct (again, old code, probably leftover since I don’t see that flag being used elsewhere anymore for quite some time)
Just for completeness, the docs did warn you to take care: OnPostprocessTexture mentions that “if you modify the TextureImporter settings in this way, it has no effect on the texture that Unity is currently importing, but it does apply the next time Unity imports this texture. This causes unpredictable results.”; basically your “restore” sets everything up for the next reimport, but doesn’t alter the (texture import) state of the current texture (hence, the system still assumes it was imported as uncompressed). Just so you know
@jonaskhalil Thank you for your thorough investigative work!
I speculate that there may be some sort of internal flag that records whether any settings have been changed since the importer last ran.
I wouldn’t speak out of turn, but I would suggest some potential fixes:
One could assume that any changes made to the import settings in OnPostProcessTexture are by definition intentional; if the settings were valid prior; and the underlying Texture is valid after, one could assume that the settings are valid, too.
if the above internal flag exists, you could have some sort of Validate() function (perhaps some inherited function from the AssetProcessor base class) where the PostProcessor signs off on a “yes this was intentional” at the end of the function. A manuallyValidated flag could be stored internally and then override some logic in that (dynamically determined) texture importer flag.
One could manually check the properties of the texture against the import settings (for instance, in the case where an AssetPostProcessor downscales the texture manually).
You are way ahead of me! The “changes only applied next time you import” thing is a problem I did encounter (cropping sprites and then changing pivot and PPU to make the sprite appear the same) but I don’t think these problems are directly related.
@Higgenbothom , thanks for thinking along with us Te first issue we need to tackle is “do we really want that info message?”. For instance, when you change compression but haven’t applied the texture importer changes yet, it also gives you that same message … This is wildly inconsistent; there are other importer changes that would also mean that the preview window is not up-to-date. For instance, if I reduce the max resolution from 1024x1024 to 64x64, why not have a similar message “not yet rescaled” ?? So first thing’s first: when do we need that info message …
if you have your project preferences set up to not compress on import, that’s definitely a valid case where we want that information there
in your case, I think it makes sense as well … If you disable compression in texture preprocess, then you want to have a message that it’s not yet compressed according to the target settings. But of course, vice versa, we then have to know if the texture gets compressed in postprocess like you are doing (and, then, not show that message anymore)
So, my gut feeling is that checking the actual texture properties is most straight-forward. My main concern: why didn’t we do it like that to begin with; maybe there’s a performance cost to checking it dynamically like that. Or it was just a choice that was made over 10 years ago and no one batted an eye (maybe back then there was no post-process compression to consider, so the used importer settings where a valid source of truth anyway).
Alternatively, your first suggestion could also make sense. Maybe I’m adapting it somewhat, but I’m thinking that “whatever happens in postprocess, should also still be applied to 'the importer settings with which a texture was imported’”. So rather than saying “the import itself happened without compression”, we could store something like “the import finished (after postprocessing) with the texture compressed”. This one, I’m also doubting for several reasons:
Maybe we’re introducing other issues by considering “everything that happens in postprocess” (then again, we could also be fixing bugs we don’t know about yet)
But more importantly, what happens if a user doesn’t compress in postprocess? What if they have a custom editor menu with a “compress all my textures now” button in there? This would still not be caught that way …
About the changes being intentional (implicitly as in your suggestion #1 or via a Validate in suggestion #2), that again pushes me towards #3 and just considering the actual texture properties as the “real, final data” (since all changes are intentional) … But, it’s all up for consideration
Another casus to consider: an AssetPostProcessor whose goal it is to change the default texture compression format. For example: someone might decide that “for the final build, I want to make all ‘default texture compression’ textures with an alpha BC7 format” (BC7 has the same perf cost as DXT5 but looks better).
Here’s what such an AssetPostProcessor might look like:
OnPreprocessTexture
if texture compression format is not auto, bypass
store what type of auto (low, medium, high) we were using
set texture format to uncompressed
OnPostprocessTexture
if we bypassed OnPreProcessTexture, bypass here too
sweep the alpha channel, checking whether or not it contains any data (or is the same value the entire way through)
if there is an alpha, manually compress to BC7
if there is NOT an alpha, manually compress to DXT1
Revert the texture import settings back to automatic (was set to “uncompressed” in OnPreprocessTexture)
It is this last step that triggers the issue. In fact, it is this same step that’s causing the issue in the general case too, but I thought this was an interesting edge case where values do not match but are still set intentionally.
This does give way to another potential solution: expose a function that allows us to revert the texture import settings to what it was before OnPreprocessTexture was invoked. This would be similar to my second proposed solution (a Validate() function), but in this case my code would be skipping step 5 (which causes the issue). This way the problem can be handled internally.
This is not a full perfect solution, because I would really like to be able to write to the pivot and PPU values in OnPostprocessTexture, and have those values be used right away instead of needing a reimport. Perhaps a system where I would revert first, and then write those two values, then end with a Validate() function. Or perhaps some (editor-only utility or helper) function where I can tell the Sprite that I want the pivot/PPU updated to something else.