Hi - long story short - GetRepackedSkin() doesn't appear to work correctly when the source textures it's packing have had their MaxSize reduced in the Texture Import Settings. (This is in 2019.4, and I've been through all the recommended notes in the documentation on GetRepackedSkin(), regarding FullRect, Read/Write enabled, etc.)
We recently started porting our game to Switch and needed to reduce the resolution of our character textures due to the memory overhead when packing them, which we do extensively. Unfortunately we ran into issues with Graphics.CopyTexture() reporting "region not fitting in source element" after overriding the platform settings for Switch.
After a lot of digging, I believe this is because Spine does not account for a reduced MaxSize in the texture import settings when repacking. More specifically, this is caused by the use of GetUnityRect() within ToTexture(), before the returned Rect is passed to CopyTexture(). The Rect returned by GetUnityRect() uses region.page.height, which will be larger than the Texture2D you are packing if its MaxSize setting has been reduced, resulting in a Rect whose dimensions will be incorrect, and likely lie outside the Texture's bounds, producing the error above.
There is a note in the XML above GetUnityRect that mentions the method "relies on region.page.height being correctly set", so this may be a known issue that I'm not dealing with correctly (if so, please let me know), but unfortunately I'm seriously short on time right now, so I'm just reporting what we're using as a workaround.
I fixed this for us by modifying ToTexture() to scale the result of GetUnityRect() by the ratio between the source texture and the page's dimensions:
public static Texture2D ToTexture (this AtlasRegion ar, TextureFormat textureFormat = SpineTextureFormat, bool mipmaps = UseMipMaps) {
Texture2D output;
CachedRegionTextures.TryGetValue(ar, out output);
if (output == null) {
Texture2D sourceTexture = ar.GetMainTexture();
Rect r = ar.GetUnityRect();
//GetUnityRect() doesn't work if the source texture's max size in its importer settings has been reduced (e.g. per platform).
//Scale the rect to account for this.
float scaleX = sourceTexture.width * 1f / ar.page.width;
float scaleY = sourceTexture.height * 1f / ar.page.height;
var scale = new Vector2(scaleX, scaleY);
r = new Rect(r.position * scale, r.size * scale);
int width = (int)r.width;
int height = (int)r.height;
output = new Texture2D(width, height, textureFormat, mipmaps) { name = ar.name };
output.CopyTextureAttributesFrom(sourceTexture);
AtlasUtilities.CopyTexture(sourceTexture, r, output);
CachedRegionTextures.Add(ar, output);
CachedRegionTexturesList.Add(output);
}
return output;
}
I would like to provide a small repro project, but unfortunately due to my current time restraints, and only having our game's source assets to work with, I am unable to. However, I imagine it would be possible to reproduce this by exporting a Spine skeleton with more than one skin, reducing each of its textures atlases' MaxSize (say from 2048 to 512) in the import settings, and calling GetRepackedSkin() on a composite Skin.
I'll also throw it out there that this might be related to your GitHub issue #1609, which I came across when trying to diagnose our problem. I haven't tested whether it would work, but given my fix adjusts the rect by reading the Texture's dimensons, I would imagine it would correctly scale any textures that had been reduced in size via Quality Settings.
I'm using Spine 2.8-2019-09-27. I did try using the latest version of AssetUtilities from github but it the results were the same.
As an aside, I would personally very much appreciate it if the GC footprint of GetRepackedSkin() could be reduced. It allocates a Dictionary and five lists on every call (at least; there may be more in any secondary methods). Considering this method won't be thread safe because of Unity anyway, I would think these collections could probably be allocated once and reused?