PluginBugs / Issues-ItemsAdder

Repository used to keep track of issues of my plugin ItemsAdder
https://itemsadder.devs.beer
51 stars 21 forks source link

Optimize Atlases #3066

Closed MinecraftAdmin closed 10 months ago

MinecraftAdmin commented 11 months ago

Terms

Discord tag (optional)

No response

Describe the solution you'd like

Right now the blocks.json atlas file generated by ItemsAdder creates a ton of "single" entries which slows down resource pack loading.

I propose it utilize directories instead to optimize loading.

Here is a blocks.json of a 35mb resource pack which loads in 3 seconds and is made up of 134 lines

Blazing fast resource pack ``` { "sources": [ { "type": "directory", "source": "entity", "prefix": "entity/" }, { "type": "directory", "source": "armor", "prefix": "armor/" }, { "type": "directory", "source": "discs", "prefix": "discs/" }, { "type": "directory", "source": "ui", "prefix": "ui/" }, { "type": "directory", "source": "emojis", "prefix": "emojis/" }, { "type": "directory", "source": "furniture", "prefix": "furniture/" }, { "type": "directory", "source": "hats", "prefix": "hats/" }, { "type": "directory", "source": "mcpets", "prefix": "mcpets/" }, { "type": "directory", "source": "custom", "prefix": "custom/" }, { "type": "directory", "source": "solar", "prefix": "solar/" }, { "type": "directory", "source": "starwars", "prefix": "starwars/" }, { "type": "directory", "source": "toffy", "prefix": "toffy/" }, { "type": "directory", "source": "akaleaf", "prefix": "akaleaf/" }, { "type": "directory", "source": "models", "prefix": "models/" }, { "type": "directory", "source": "yungwilder", "prefix": "yungwilder/" }, { "type": "directory", "source": "independence_set", "prefix": "independence_set/" }, { "type": "directory", "source": "independence_armor", "prefix": "independence_armor/" }, { "type": "directory", "source": "halloween_pack", "prefix": "halloween_pack/" }, { "type": "directory", "source": "glass_armor", "prefix": "glass_armor/" }, { "type": "directory", "source": "winter_discs", "prefix": "winter_discs/" }, { "type":"directory", "source":"nr", "prefix":"nr/" }, { "type":"directory", "source":"modelengine", "prefix":"modelengine/" }, { "type":"directory", "source":"galaxy_pack", "prefix":"galaxy_pack/" }, { "type":"directory", "source":"vader_armor", "prefix":"vader_armor/" }, { "type":"directory", "source":"lun", "prefix":"lun/" }, { "type":"directory", "source":"lunarset", "prefix":"lunarset/" }, { "type":"directory", "source":"animations", "prefix":"animations/" } ] } ```

Here is a snippet of blocks.json from my resource pack (8mb) generated using ItemsAdder which contains 6381 lines (!)

Snippet of my slow pack ``` { "sources": [ { "source": "entity", "prefix": "entity/", "type": "directory" }, { "type": "single", "resource": "server:enchanter_icons/stat_track" }, { "type": "single", "resource": "beatsset:bow_1" }, { "type": "single", "resource": "beatsset:bow_2" }, { "type": "single", "resource": "traditional_furniture:computers/laptops/laptop_black" }, { "type": "single", "resource": "littlecatset:hood" }, { "type": "single", "resource": "dragonsoulset:crossbow" }, { "type": "single", "resource": "lunarset:hoe" }, { "type": "single", "resource": "beatsset:spear" }, { "type": "single", "resource": "traditional_furniture:computers/laptops/laptop_gray" }, { "type": "single", "resource": "saintpatricksset:greatsword" }, { "type": "single", "resource": "lzfurniture:furniture/plants/plants_bigbox_violets" }, { "type": "single", "resource": "traditional_furniture:dressers/dresser_crimsoner" }, { "type": "single", "resource": "server:ranks/diamond" }, { "type": "single", "resource": "traditional_furniture:nightstands/nightstand_crimsoner" }, { "type": "single", "resource": "itemshopplus:itemshopplus/yellowshop" }, { "type": "single", "resource": "sweetheartset:wings" }, { "type": "single", "resource": "minerals_pack:deepslate_malachite_ore" }, { "type": "single", "resource": "discord:wumpus_chestplate" }, { "type": "single", "resource": "cosmetic:blockville/canes/2/zombie_plushy" }, { "type": "single", "resource": "tools_armors_pack:iridium_pickaxe" }, { "type": "single", "resource": "littleduckset:spear" }, { "type": "single", "resource": "server:icon_left_blue" }, { "type": "single", "resource": "saintpatricksset:trident" }, { "type": "single", "resource": "tools_armors_pack:jade_axe" }, { "type": "single", "resource": "littlesharkset:bow" }, { "type": "single", "resource": "lzfurniture:furniture/medievalroyal/medievalroyal_sofa" }, { "type": "single", "resource": "lzfurniture:furniture/mushroom/mushroom_table" }, { "type": "single", "resource": "modelengine:entity/toro_wither_pool" }, { "type": "single", "resource": "modelengine:entity/wumpus_engine" }, { "type": "single", "resource": "cosmetic:custom/menu/empty" }, { "type": "single", "resource": "server:charger" }, { "type": "single", "resource": "_iainternal:icons/icon_arrow_chest" }, { "type": "single", "resource": "cosmetic:blockville/hats/dreadlocks" }, { "type": "single", "resource": "cosmetic:custom/item/brush" }, { "type": "single", "resource": "cosmetic:blockville/hats/3/viking_helmet" }, { "type": "single", "resource": "akiraset:hoe" }, { "type": "single", "resource": "littlebunny_set:bunny_glove" }, { "type": "single", "resource": "modelengine:entity/vase_common" }, { "type": "single", "resource": "lzfurniture:furniture/japanese/japanese_folding_screen" }, { "type": "single", "resource": "furnituresplus:furnituresplus/yellowpaint" }, { "type": "single", "resource": "itemshopplus:itemshopplus/wool/yellow_wool" }, { "type": "single", "resource": "littlepandaset:armor/littlepanda_chestplate_icon" }, { "type": "single", "resource": "thunderboltset:hoe" }, { "type": "single", "resource": "server:gray" }, { "type": "single", "resource": "traditional_furniture:lamps/standing/standing_lamp_orange" }, { "type": "single", "resource": "server:allay" }, { "type": "single", "resource": "server:emoji_balloons/balloon_emoji_wink" }, { "type": "single", "resource": "furnituresplus:furnituresplus/cyanfabric" }, { "type": "single", "resource": "cosmetic:blockville/backpacks/1/treadmill" }, { "type": "single", "resource": "littleduckset:pickaxe" }, { "type": "single", "resource": "traditional_furniture:dressers/dresser_birch" }, { "type": "single", "resource": "enderdragon:enderdragon_shield" }, { "type": "single", "resource": "saintpatricksset:armor/saintpatrick_boots_icon" }, { "type": "single", "resource": "slime_set:slime_sword/0_green/texture_00" }, { "type": "single", "resource": "lzfurniture:furniture/area/area_sofa_double_blue" }, { "type": "single", "resource": "lzfurniture:furniture/kitchen/kitchen_sink" }, { "type": "single", "resource": "cosmetic:blockville/canes/redpanda_claw" }, { "type": "single", "resource": "itemshopplus:itemshopplus/terra/cyan_terracotta" }, { "type": "single", "resource": "cosmetic:blockville/hats/3/football_helmet_red" }, { "type": "single", "resource": "modelengine:entity/sabertooth_savanna" }, { "type": "single", "resource": "lzfurniture:furniture/goth_furniture/dressing_table" }, { "type": "single", "resource": "dragonsoulset:bow_2" }, { "type": "single", "resource": "dragonsoulset:bow_1" }, { "type": "single", "resource": "jobs:chestlocked" }, { "type": "single", "resource": "discordnitroset:shield" }, { "type": "single", "resource": "server:ravager" }, { "type": "single", "resource": "lzfurniture:furniture/kitchen/kitchen_winerack" }, { "type": "single", "resource": "littlepandaset:greatsword" }, { "type": "single", "resource": "tools_armors_pack:amber_hoe" }, { "type": "single", "resource": "server:enchanter_icons/transmog_scroll" }, ... ] } ```

Is your feature request related to a problem?

No specific problem, just slow resource pack loading times.

Describe alternatives you've considered

I spoke with the creator of PackSquash and they're willing to implement atlas optimization down the line, but since ItemsAdder has raw path data, it should be able to do it natively.

Additional context

No response

github-actions[bot] commented 11 months ago

Hello, your report will be looked into as soon as possible. Please do not bump this thread and don't post it on multiple platforms.

Thanks a lot for your patience.

LoneDev6 commented 11 months ago

The reason of the atlas generated like that is because some resourcepacks creators put font images (glyphs, characters) png files into the same folders as blocks, items and furniture textures. Font images are known to not respect the power of 2 dimensions. Making the game load all textures in these folders mean that the font images would break the mipmaps, causing the game to look crispy.

What I could do is implement what you asked but add a check to use the single-png file logic if the folder contains any font image png. This would stop the mipmap issue and the slow loading issue.

What do you think?

MinecraftAdmin commented 11 months ago

Yes please! That would be truly revolutionary for pack loading times, a major upgrade!

AlexTMjugador commented 11 months ago

Hey! Since I was indirectly mentioned in this issue, I thought I might as well chime in and give my two cents :smile:

What I could do is implement what you asked but add a check to use the single-png file logic if the folder contains any font image png. This would stop the mipmap issue and the slow loading issue.

I think that's a good idea, although I also wonder how feasible it would be with ItemsAdder knowledge about the pack to move the offending files to separate directories, that don't belong to these directory atlases. That way there would be no need to resort to atlases with single textures, but this idea came to me in a few seconds and I don't know how feasible it is to implement at all, so please excuse me if it's not that useful :sweat_smile:

LoneDev6 commented 11 months ago

to move the offending files to separate directories

this is smarter, I can do that for sure :D

LoneDev6 commented 11 months ago

Here is my test

Using `directory` method - 22 seconds [15:34:39] [15:35:01] (Note: I cannot use folders method for all the textures as some are in the root of `textures` folder, so there is no other way to reference them other than `single` method ```json { "sources": [ { "source": "entity", "prefix": "entity/", "type": "directory" }, { "source": "furniture", "type": "directory", "prefix": "furniture/" }, { "source": "paintings", "type": "directory", "prefix": "paintings/" }, { "source": "ak47", "type": "directory", "prefix": "ak47/" }, { "source": "christmas", "type": "directory", "prefix": "christmas/" }, { "source": "halloween", "type": "directory", "prefix": "halloween/" }, { "source": "icons", "type": "directory", "prefix": "icons/" }, { "source": "back", "type": "directory", "prefix": "back/" }, { "source": "hat", "type": "directory", "prefix": "hat/" }, { "type": "single", "resource": "iaweapons:clip" }, { "type": "single", "resource": "iavehicles:broomstick_dark_dead_bush" }, { "type": "single", "resource": "iavehicles:shopping_cart" }, { "type": "single", "resource": "iaweapons:hand_gun" }, { "type": "single", "resource": "iaweapons:projectile" }, { "type": "single", "resource": "iaweapons:ak47/handle" }, { "type": "single", "resource": "iaweapons:grenade" }, { "type": "single", "resource": "iaweapons:shotgun" }, { "type": "single", "resource": "iaweapons:revolver" }, { "type": "single", "resource": "iaweapons:shotgun_cartridge" } ] } ```
Using `single` method 10 seconds [15:35:35] [15:35:45] ```json { "sources": [ { "source": "entity", "prefix": "entity/", "type": "directory" }, { "resource": "cosmetics:entity/star_kite/star_kite", "sprite": "cosmetics:entity/star_kite/star_kite", "type": "single" }, { "type": "single", "resource": "iadeco:furniture/black_chandeliers/fire" }, { "type": "single", "resource": "iadeco:paintings/stage" }, { "type": "single", "resource": "iawearables:back/red_backpack" }, { "type": "single", "resource": "iafestivities:halloween/pumpkin" }, { "type": "single", "resource": "iawearables:hat/glasses" }, { "type": "single", "resource": "iafestivities:christmas/candy" }, { "type": "single", "resource": "entity/villager/profession/farmer" }, { "type": "single", "resource": "iadeco:furniture/black_and_red_chandeliers/fire" }, { "type": "single", "resource": "_iainternal:icons/icon_arrow_chest" }, { "type": "single", "resource": "iawearables:back/black_backpack" }, { "type": "single", "resource": "iaweapons:clip" }, { "type": "single", "resource": "iadeco:furniture/school_chair_lazy/toy" }, { "type": "single", "resource": "iafestivities:halloween/bones_backpack/skull" }, { "type": "single", "resource": "modelengine:entity/paladin_wings" }, { "type": "single", "resource": "iafestivities:halloween/pumpkin_side" }, { "type": "single", "resource": "iafestivities:halloween/dark_jack_o_lantern_backpack/pumpkin_side" }, { "type": "single", "resource": "modelengine:entity/barman_robot" }, { "type": "single", "resource": "iawearables:back/blue_backpack" }, { "type": "single", "resource": "iafestivities:halloween/pumpkin_stem_connected" }, { "type": "single", "resource": "iafestivities:halloween/bat_wings" }, { "type": "single", "resource": "iavehicles:broomstick_dark_dead_bush" }, { "type": "single", "resource": "iafestivities:christmas/christmas_wreath" }, { "type": "single", "resource": "iawearables:back/black_cat_tail" }, { "type": "single", "resource": "iavehicles:shopping_cart" }, { "type": "single", "resource": "iafestivities:halloween/dark_jack_o_lantern_backpack/pumpkin_stem_connected" }, { "type": "single", "resource": "_iainternal:icons/icon_left_blue" }, { "type": "single", "resource": "iaweapons:hand_gun" }, { "type": "single", "resource": "iawearables:back/turquoise_backpack" }, { "type": "single", "resource": "iafestivities:christmas/christmas_tree/red_orb" }, { "type": "single", "resource": "iadeco:paintings/match" }, { "type": "single", "resource": "modelengine:entity/rocky" }, { "type": "single", "resource": "iawearables:hat/deadmau5_hat" }, { "type": "single", "resource": "modelengine:entity/ballon_cluster" }, { "type": "single", "resource": "iafestivities:christmas/christmas_candle/fire" }, { "type": "single", "resource": "iafestivities:halloween/axe_hat" }, { "type": "single", "resource": "modelengine:entity/pudgelin" }, { "type": "single", "resource": "iadeco:furniture/school_chair_lazy/book" }, { "type": "single", "resource": "iawearables:back/demoniac_red_wings" }, { "type": "single", "resource": "iafestivities:halloween/spider_backpack" }, { "type": "single", "resource": "iadeco:furniture/small_fountain/water_flow" }, { "type": "single", "resource": "entity/skeleton/wither_skeleton" }, { "type": "single", "resource": "iawearables:hat/cigar" }, { "type": "single", "resource": "iafestivities:christmas/christmas_tree/green_orb" }, { "type": "single", "resource": "_iainternal:icons/icon_right_blue" }, { "type": "single", "resource": "iafestivities:halloween/decorative_pumpkin/fire" }, { "type": "single", "resource": "modelengine:entity/piranha_plant" }, { "type": "single", "resource": "iawearables:hat/mining_helmet" }, { "type": "single", "resource": "_iainternal:icons/icon_next_orange" }, { "type": "single", "resource": "iaweapons:projectile" }, { "type": "single", "resource": "iadeco:paintings/courbet" }, { "type": "single", "resource": "iadeco:paintings/back" }, { "type": "single", "resource": "cosmetics:entity/star_kite/star_kite" }, { "type": "single", "resource": "iafestivities:christmas/christmas_candle/leaves" }, { "type": "single", "resource": "_iainternal:icons/icon_cancel" }, { "type": "single", "resource": "iawearables:hat/biker_helmet" }, { "type": "single", "resource": "iafestivities:halloween/witch_hat" }, { "type": "single", "resource": "iafestivities:halloween/bones_backpack/bone" }, { "type": "single", "resource": "iaalchemy:furniture/mysterious_stone/symbols" }, { "type": "single", "resource": "iawearables:back/white_cat_tail" }, { "type": "single", "resource": "iaweapons:ak47/handle" }, { "type": "single", "resource": "iawearables:back/purple_backpack" }, { "type": "single", "resource": "iadeco:paintings/graham" }, { "type": "single", "resource": "iaweapons:grenade" }, { "type": "single", "resource": "iadeco:paintings/void" }, { "type": "single", "resource": "modelengine:entity/amogus" }, { "type": "single", "resource": "iawearables:hat/cat_ears" }, { "type": "single", "resource": "entity/chest/normal" }, { "type": "single", "resource": "iawearables:back/demoniac_black_wings" }, { "type": "single", "resource": "_iainternal:icons/icon_search" }, { "type": "single", "resource": "iawearables:back/demoniac_blue_wings" }, { "type": "single", "resource": "iadeco:paintings/wanderer" }, { "type": "single", "resource": "modelengine:entity/locomotive" }, { "type": "single", "resource": "iawearables:hat/cool_sunglasses" }, { "type": "single", "resource": "iawearables:back/demoniac_purple_wings" }, { "type": "single", "resource": "iaweapons:shotgun" }, { "type": "single", "resource": "iadeco:paintings/pool" }, { "type": "single", "resource": "entity/enderdragon/dragon" }, { "type": "single", "resource": "iaweapons:revolver" }, { "type": "single", "resource": "iadeco:furniture/small_fountain/water_still" }, { "type": "single", "resource": "iawearables:back/demoniac_turquoise_wings" }, { "type": "single", "resource": "iadeco:furniture/school_chair_lazy/book_paper" }, { "type": "single", "resource": "iawearables:back/brown_backpack" }, { "type": "single", "resource": "iaweapons:shotgun_cartridge" }, { "type": "single", "resource": "iawearables:back/all_black_cat_tail" }, { "type": "single", "resource": "iaweapons:ak47/body" }, { "type": "single", "resource": "iadeco:paintings/skull_and_roses" }, { "type": "single", "resource": "iadeco:furniture/white_chandeliers/fire" }, { "type": "single", "resource": "iafestivities:halloween/cave_spider_backpack" }, { "type": "single", "resource": "modelengine:entity/skeleton1" }, { "type": "single", "resource": "iafestivities:christmas/christmas_tree/leaves" }, { "type": "single", "resource": "_iainternal:icons/icon_back_orange" }, { "type": "single", "resource": "iafestivities:halloween/dark_jack_o_lantern_backpack/pumpkin" }, { "type": "single", "resource": "iawearables:hat/ruby_pickaxe_hat" }, { "type": "single", "resource": "iadeco:paintings/burst" } ] } ```
LoneDev6 commented 11 months ago

There are some downsides of this feature. directory type sources cannot specify a namespace. This causes the atlas entry to be working on every namespace which has this particular folder.

For example it will load every texture in the gui folder of any namespace if you add this specific setting: { "type": "directory", "prefix": "gui/", "source": "gui" },

In this example the issue is related to the fact that Minecraft vanilla has a gui texture which contains non power of 2 textures. This causes the game to throw this in logs:

[17:31:29] Texture minecraft:gui/info_icon with size 20x20 limits mip level from 4 to 2
[17:31:29] Texture realms:gui/realms/user_icon with size 8x14 limits mip level from 2 to 1

The only solution to avoid this is for me to hardcode specific blacklist for some textures to avoid this from happening, it's a bit dirty but I guess it's the only way. This would cause confusion and be keen to bugs.

Is this change really worth?

MinecraftAdmin commented 11 months ago

May I send you the resource pack so you can analyze if/how the atlas is making it so quick to load in the client? I don't want to post it here as it is from a server I do not own. My Discord is: ultimateop

LoneDev6 commented 11 months ago

Send friend request as I can't find you on my Discord server

LoneDev6 commented 11 months ago

I did some benchmark with the provided pack and there is no improvement in loading performance, it just introduces some downsides to the logic which will eventually confuse admins and resourcepacks creators. single type is not causing any performance drop.

I think that 8KB of blocks.json atlas is a very low size to justify introducing this new unsafe feature.

MinecraftAdmin commented 11 months ago

I have posted my resource pack and a video with the load speed difference to our Discord DMs.

LoneDev6 commented 11 months ago

benchmark: https://pastebin.com/cCYzb9r9

old single method
[19:30:08]
[19:30:09]
1 second

https://pastebin.com/egLLHGyZ

new dirs method
[19:30:57]
[19:30:58]
1 second

I can't manage to get any improvement. Anyway, as a side note, the game code would be extremely dumb if a recursive directory search would be faster than a direct png file reference. What do you think @AlexTMjugador ?

AlexTMjugador commented 11 months ago

Anyway, as a side note, the game code would be extremely dumb if a recursive directory search would be faster than a direct png file reference. What do you think @AlexTMjugador ?

The process of stitching textures into atlases that Minecraft does when loading a resource pack can be time consuming, and I have seen several cases where it is the main bottleneck when loading packs, so there is some value in carefully profiling how these are constructed. The cost of creating and uploading multiple textures to the GPU vs. stitching multiple sprites into one large atlas texture to send to the GPU is what matters most here, not the enumeration of sprite files themselves on the file system.

With that said, I didn't look at what exactly causes the difference in load times for the packs @MinecraftAdmin mentioned. My intent was to point out a likely cause for performance issues.

To properly define this issue and what can be done about it, it would be essential to get an "apples to apples" comparison of several pairs of resource packs, where the only difference between those pairs is the atlas method used. That way, it would be possible to isolate whether the performance difference is due to atlases or something else, such as model definitions or other pack content that's forcing the game to load the assets in a different way. Pack size alone is not a good proxy for how fast the game should be able to load a pack once it's downloaded.

@MinecraftAdmin, how feasible would it be to change the fast-loading resource pack to use single texture atlases, as ItemsAdder would do, and compare the resulting loading time differences? That could give more precise insight on what's causing these differences.

Edit: it'd be also nice to mention what game versions were used for these tests. Minecraft has been changing internal details on how atlases are constructed on the last versions.

LoneDev6 commented 11 months ago

it would be essential to get an "apples to apples" comparison of several pairs of resource packs

yup! I did it and I get no difference in both methods.

it would be essential to get an "apples to apples" comparison of several pairs of resource packs

Was tested with 1.20.1 and 1.20.2

AlexTMjugador commented 11 months ago

yup! I did it and I get no difference in both methods.

Great! :+1: It looks like atlases do not explain the whole loading performance story in that benchmark then, at least on Minecraft 1.20.

However, I noticed that the single atlases file here takes 1718 lines, while the OP mentioned that their blocks.json was 6381 lines long, suggesting that it was more packed with content. Maybe the results change when the pack gets bigger, when the cost of uploading so many textures exceeds the cost of stitching them together into one?

LoneDev6 commented 11 months ago

Basically the issue is that this game is dumb and directory atlas entry type won't allow you to specify a namespace. This results in making the game search for the png files in ANY namespace, it just checks if the sub-folder exists. This leads to multiple unintended issues I explained here: https://github.com/PluginBugs/Issues-ItemsAdder/issues/3066#issuecomment-1757976246

LoneDev6 commented 11 months ago

I have no idea what the fuck Mojang smoked when they coded this, but it is what it is. The only reliable way I found is using single type.

AlexTMjugador commented 10 months ago

Fair enough! I don't have any insight into why directory atlases are designed this (in my opinion also somewhat strange) way, but if single texture atlases don't cause the reported slowdons, it's perfectly fine to use them 😄