A texture dumped for a texture pack had its 4, 5 and 6-bit channels
shifted up to 8, which leaves the low bits empty: white came out as 248,
or 240. What is drawn has the top bits repeated in them, and white is
255. The dump does the same.
The loader thread worked out the hash a texture's replacement is filed
under itself: from video memory, which with threaded rendering the game
is writing again by then, and from the texture's own fields, which the
renderer may be updating for the next request. It also looked at the
texture's "dirty" stamp, a plain variable of the renderer's.
The renderer does the hashing now, when it asks - it is updating the
texture at that moment, and the game is held while it does - and leaves
the three names for the loader in the texture before it counts the
request. The loader reads the count and then the names, loads, and
takes its count off: if a request came in meanwhile, the count it took
off is not all there was, it goes round again, and what it loaded from
names that may have been half one request's and half the next's is
dropped. The image and its size are still only looked at by the renderer
once the count is back to zero. Nothing else of a texture is read by the
loader.
(The "dirty" check saved loading for a texture that had been invalidated
since it asked. Such a texture asks again when it is next updated, and
the image loaded for nothing is freed then, or with the texture.)
tools/threads: the model of the loader's queue carries a three-word
request the same way and checks that what was made of each node's last
request is that request's, whole; under TSan and ASan + UBSan.
Run: the test disc with a pack of sixteen magenta replacements for the
textures it dumps, threaded rendering on - the screenshot has 41,148
magenta pixels against 24,228 without the pack; with the option on and
no pack, 24,228.
Not done: the audit's bound on what is queued. A texture is on the list
at most once and holds at most one loaded image, so both are bounded by
the number of textures, not by bytes.
Whether a game has custom textures was a plain bool that the loader
thread wrote once it had listed the directory and the renderer read; and
nothing cleared it when the game went. After a game with a texture pack,
one without inherited "available": its textures were queued for a loader
that had not been started, and a texture waiting on the loader cannot be
freed. The same if the loader thread could not be started.
It is an atomic now, cleared (with the path) when the game goes, and
cleared again if the thread does not start.
Not in this: the loader still works out a texture's hash itself, from
video memory the game may be writing. That is its own change.
- A compressed texture's size was half its pixels' share and left the
codebook out. The size is what video memory is watched by: a write
to the rest of the texture went unseen and the texture stayed as it
was. It is the codebook and a byte for every four pixels.
The size was also what the texture's name for custom textures was
hashed from, so two compressed textures could share a name. The
name is now the hash of the codebook and of the whole texture, the
same as upstream's; the old name is kept as a second one to look a
custom texture up by, so texture packs made before still load.
- A texture both compressed and planar was taken for a mistake and
its compression ignored. It is decoded as what it is: an entry of
the codebook is four pixels in a row. Upstream: the Genesis emulator
in the Teenage Mutant Ninja Turtles collection.
- Compressed and mipmapped, a texture is twiddled whatever the scan
order bit says. Upstream: corrupted textures and a crash in Star
Wars Demolition.
- The 1x1 level of a compressed YUV texture is taken from the 2x2
level; its own bytes are not a colour. Upstream: magenta background
textures in World Series Baseball 2K2.
- A stride of zero in TEXT_CONTROL means the texture's own width, not
a texture of no size.
The converters themselves were compared with upstream's first, each
one on the same bytes, and are the same; the new planar compressed
ones were checked the same way (tools/rend, next commit). The live
test passes on gl and glcore; the Soul Calibur state, which is mostly
compressed textures, runs with no texture refused.
One difference from upstream is kept: for a paletted texture the name
still includes the bit upstream leaves out, so the names this core
has given such textures do not change.
A track can show a sector where it already is: a raw track in its
mapping or in the archive member it was decoded into, a CHD track in
the decoded hunk. ReadSectors() converts straight from there into the
destination, so a sector read is one copy; a track that cannot show
the sector is read through the buffer as before.
The ELF loader parses the file in its mapping and custom textures are
decoded from theirs, each read once only when there is no mapping.
Three were left in the core's own sources: the count of custom-texture
load requests in BaseTextureCacheData, the reset request flag, and an
unused <atomic> include in gles.h. They are retro_atomic like the rest;
the mirror of the texture counter in tools/threads/threads_test.cpp goes
the same way. No change in behaviour.
The render thread queued textures for the custom texture loader thread
in a vector under a mutex. The queue is now cMpscList
(core/lockfree.h): a push is one compare-and-swap, the loader takes
everything queued in one exchange, and the link lives in the texture so
nothing is allocated.
A texture is on the list at most once. It is pushed when its
custom_load_in_progress count leaves zero; requests that arrive while
it is queued or being loaded only raise the count, and the loader goes
round again until it has answered them all.
Terminate() used to empty the queue without lowering those counts, so
a texture that was still waiting stayed "loading" for good: it could
not be deleted and never showed its custom image. The loader and
Terminate() now clear the count of everything they drop.