Closed einarf closed 3 years ago
Ooh, this will be a big change. I'm all on-board.
Currently playing around in the ui_development
(locally) branch. Could be we need a separate branch for this.
EDIT : The branch is texture-atlas
Closing this for now. Atlases are definitely in 2.6, but there are some remaining improvements we know about.
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 inatlas.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 theArcadeContext
. This can be accessed throughwindow.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 thedefault_atlas
property is accesed.All
SpriteList
s will use this atlas unless a differentTextureAtlas
instance is passed toSpriteList(..., 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 thetexture_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
add(texture)
methodborder
parameter (default 1). This puts all the textures 1 pixel apart in the atlas to avoid interpolation bleedingSpriteList.preload_textures
can just inject the textures in the atlaspack()
method that will reduce the current atlas to a minimum size?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 asflipped
,mirrored
etc.. and generate different texture coordinates.This means that different
texture_id
s can reference the same texture in vram, but with different texture coordinates. We really need to explore how theTexture.name
is used everywhere. It's currently a unique key for the texture in atlases. We might need to introduce a separateatlas_key
and add additional attributes. Note that the texture coordiantes needs to be transformed in the same order as in the textureWe 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
shuffle
is broken + suffle in start end rangereverse
is broken + reverse in start/end rangeSpriteList.clear()
Sprite.sprite_liste
should probably be aWeakSet