pythonarcade / arcade

Easy to use Python library for creating 2D arcade games.
http://arcade.academy
Other
1.72k stars 330 forks source link

Add proper texture atlas support #850

Closed einarf closed 3 years ago

einarf commented 3 years ago

Texture Atlases

NOTE: This change is not compatible with the current text rendering in arcade. This task depends on #851

Current State

Currently we don't have a type representing a texture atlas. By texture atlas we mean a type that is responsible for managing a large texture in vram containing multiple textures and providing information about where these textures are located in the large texture.

SpriteList currently do build atlases on the fly internally when we detect a new texture for one of the sprites. The method is however not very efficient as the atlas is built all the way from scratch every time. It's smart because we save memory, but the performance implications are pretty devestating in situations where texture changes are happening rapidly.

This approach aslo makes it much harder to efficiently optimize texture usage between several sprite lists. Since a sprite list have their own unique atlas internally we end up storing the same texture in multiple atlases if a sprite with the same texture is used in multiple sprite lists.

New Approach (WIP suggestion)

We introduce TextureAtlas located in atlas.py in the package root. It should try to reuse as much as possible from pyglet such as the allocation system (depending on what version of pyglet we depend on for 2.6). The allocator is row based and pretty much exactly the same as the spritelist is using internally.

How does this affect SpriteList?

Instead of each SpriteList having a separate atlas we introduce a global atlas stored in the ArcadeContext. This can be accessed through window.ctx.default_atlas. The default atlas can be 8192 x 8192 (RGBA) making room for a large amount of sprites. More advanced users should be able to override this. Optionally we can make the default atlas grow in size, but avoiding that for interation 1. We will only create the default atlas if the default_atlas property is accesed.

All SpriteLists will use this atlas unless a different TextureAtlas instance is passed to SpriteList(..., atlas=atlas). This means that for most users there will never be any atlas rebulding. More advanced users will have fine grained control over their sprite lists.

We also want to radically change how texture coordinates are stored. We should no longer store texture coordinates in the vertex array buffers. Instead we store a simple texture_id. The actual texture coordiantes will be stored in a 32 bit floating point RGBA texture. The texture atlas is responsible for updating this data. This means we avoid calculating and rebulding texture coordinates multiple times. We still need to build the texture_id buffer. The unique ids are generated per atlas for now.

A positive sideeffect is that Sprite.draw() will be much faster when global atlas is used.

We also need to make sure all animation frames are loaded for animated textures. Everything to avoid atlas modifications after initialization. Still.. if this do happens the impact should be drastically reduced since we're not doing full rebuilds.

Drawbacks

Notes

NOTE: We need to be very careful with initialization order. Many arcade types are designed to be initialized before the window is created

Documentation

We need to properly document texture atlases. Not only api docs, but also tutorials and examples.

More Enhacements

There are other enhacements that can be done over time. For example : Textures can be loaded flipped, mirrored etc. This is for example useful when you want an animate character to face multiple directions from the same texture file. The transformed arcade.Texture probably still neeeds to be there, but we don't need to store it multiple times in the atlas. Instead we can look at attributes on the texture such as flipped, mirrored etc.. and generate different texture coordinates.

This means that different texture_ids can reference the same texture in vram, but with different texture coordinates. We really need to explore how the Texture.name is used everywhere. It's currently a unique key for the texture in atlases. We might need to introduce a separate atlas_key and add additional attributes. Note that the texture coordiantes needs to be transformed in the same order as in the texture

We should also consider how texture atlases affects tmx maps. There might be things we can do during the spritelist creation for the layers. For example: Query the minimum texture atlas needed to pack all the textures and create a separate atlas for the tmx maps.

We can also improve the row allocator by rotating textures. If you add a 1 x 8000 texture currently the atlas would pretty much be full.

TODO

pvcraven commented 3 years ago

Ooh, this will be a big change. I'm all on-board.

einarf commented 3 years ago

Currently playing around in the ui_development (locally) branch. Could be we need a separate branch for this.

EDIT : The branch is texture-atlas

einarf commented 3 years ago

Closing this for now. Atlases are definitely in 2.6, but there are some remaining improvements we know about.