The shader cache keeps its compile items in chunks and reuses them, so a
shader or pipeline compiled while the game runs allocates nothing. Each
item embeds the compiler's link and callbacks, so a finished one is pushed
back as it is, and a pipeline waiting on its stages queues itself again.
The compiler keeps the waiting items in a heap by priority and order, and
is a member of the shader cache rather than an object of its own.
Vertex formats are deleted as the backend's own class, like the other GPU
objects. The vertex loader's shuffle table is a plain array, and the D3D
bounding box writes its offset as the UINT the box takes.
A backend is handed the ShaderCode: Vulkan and D3D write their header
into the room kept in front of the text and compile the two as one span,
and GL hands the header, any prefix and the text to the driver as they
are and keeps the text's block for its link messages.
ShaderText is one growing block of chars with room kept in front for a
header. ShaderCode writes into it as it is or printf-style, without fmt
or std::string; the generators' string parameters, tables and switch
trees are plain C strings, and the UID enumerations fill arrays.
SPIR-V and DXBC stay in the block rslc returns instead of being copied
into vectors, and the disk cache is lent the code to write. Code read
back from the cache is copied once into a block of its own.
Each backend's are final and called by the backend in use; the Null
backend uses the base classes, which count nothing. Bounding box values
are read into and written from the caller's arrays, and GL's queries
are one class for each kind of query object.
Each batch maps its slices of a vertex and an index stream when it
starts and unmaps them at its commit. While mapped they are off the
input assembler, for the draws a batch's textures may make.
XFB copies kept only in VRAM go with the texture cache when the video
backend is made again or a state is loaded. RAM then holds only the
uninitialized marker, so no picture is fetched from it and the frontend
keeps the one it shows until the game's next XFB copy.
Where GL has buffer storage, sync objects and texture storage, a 32 MiB
unpack buffer mapped for good in client memory is what loads of
uncompressed 2D and array textures are decoded into; glTexSubImage
uploads them from it.
AbstractTexture::LendLoad lends the upload stream's memory for a level,
and CommitLoad uploads it: Vulkan for any rows, Direct3D 12 for rows at
its pitch alignment. The texture cache decodes textures and XFBs into
it; it decodes into its own buffer and Loads that where the backend
lends none, or when the arbitrary mipmap detection reads the levels
back. The detector keeps its levels in a fixed array.
Texels are written into it in place, tile by tile, so it is cached
memory: VK_HEAP_UPLOAD_CACHED on Vulkan, a write-back L0 custom heap on
Direct3D 12, the upload heap where the device has none.
AbstractGfx calls into the Gfx of the backend in use, by
g_video_backend_kind; each backend's Gfx is final, so these are direct
calls, and what a backend does not do itself falls to the defaults,
which a backend may also call as the Base* functions. The Gfx is deleted
as its own class. The unused fullscreen calls are gone, and BackendObjects
follows the tree's formatting.
Shaders, pipelines, textures, staging textures and framebuffers call
into the classes of the backend in use, which each backend's Gfx sets
as g_video_backend_kind; those classes are final, so the calls are
direct. A std::default_delete specialization deletes them as their own
class. The Null and Software renderers use the base shader and pipeline
types.
A GameCube port can take a Game Boy Advance; its buttons are on the
port's RetroPad and it boots from gba_bios.bin in the system directory
when that is there. Its screen is drawn along the bottom right of the
picture (GBA Screens option): mGBA renders each frame straight into
upload memory the GPU thread lends it and keeps mapped, swapped in
through one atomic word, and a buffer goes back to the GBA only once
the GPU has copied from it. Without such memory the frames are uploaded
from plain buffers. The buffers are taken back, with the GBA's thread
letting go first, before the video backend goes.
Port devices change through the config, which the SI applies on the
CPU thread.
A staging texture says whether its upload memory stays mapped, and
StagingTransferDone tells without waiting whether the GPU is done with
its last copy, from the Vulkan fence counter, the Direct3D 12 fence
value or the GL sync object. Direct3D 12 upload heaps stay mapped
through their copies.
DBAT views go in a fixed array; page-table views and the large-page
translations in two-level tables per guest page, blocks allocated on
first use and kept until shutdown. GetPointerAndSize lends a pointer
and the bytes left in its region.
The GPU thread publishes a picture's description into one of four slots
and the generation naming it into an atomic word; the frontend's thread
claims it with a CAS that sets a taken bit, and the GPU thread reclaims
an untaken target with a CAS on the same word, so exactly one of them
owns it. What is being handed over and what was handed are atomic
tags only the frontend's thread writes, the first before its claim.
No slock on either side.
The frontend picks the adapter and hands over its device, so the core no
longer loads dxgi.dll, creates DXGI factories, enumerates adapters or
creates throwaway D3D11/D3D12 devices to query AA modes and logic op
support at load. Until the device arrives the AA modes are the counts
every device has; the context reset fills them in from the real device
(D3D12 now too) and re-validates the MSAA setting against them.
Nothing in the core starts a recording or a playback, so a movie is
never active. Each call site keeps its no-movie path: no determinism
toggle, the real clock for the RTC, the configured memory cards, no
pad/Wiimote input mirroring into display strings on every poll. The
state keeps the movie counters' bytes, written as zeroes, so existing
savestates still load.
D3D11 sets logic op support from the device again (off on UWP) and
always reports object names: the UWP check's else had swallowed the
object-name line and logic op was never on. The Android input backend
is no longer created, so its source, include and CIFACE_USE_ANDROID go.
Core.h declarations indented with spaces like the rest of the file.
Serial devices lend their Rx bytes in place and take Tx as pointer plus
size; JVS frames unescape onto the stack and are consumed without a
scope guard. IO port inputs and outputs are plain pointers with fixed
counts. Mediaboard addresses resolve to a lent pointer and size; the
game table is a flat array. Bluetooth passthrough keeps its link keys
in a fixed sorted array, links live transfers through the header that
already fronts each buffer, and lends ACL packets as pointer and size.
AsU8Span/AsWritableU8Span are gone from BitUtils.
SHA1, HMAC, hex dumps, PNG loading, file search, DHCP options, HTTP
posts and DirectIOFile reads and writes take pointer plus size; callers
pass their buffers straight through. Contains.h is gone; lookups are
plain loops. File search and FAT FST sorting are in-place heap sorts;
min/max/clamp/copy/fill become ternaries, memcpy and memset. Watch
string import and export had no callers and are removed.
Names, game IDs, patch lists, LBA ranges and hash exceptions are passed
as pointer plus count. min/max/fill/copy become ternaries, memset and
memcpy; FST entries sort with an in-place heap sort, partitions with an
insertion sort. WAD content checks decrypt and hash in 2 KiB chunks
straight from the borrowed input; banner names read onto the stack.
Guest handles index one of sixteen host file slots; the last handle to
close a path closes its file. No shared_ptr, weak_ptr or map. Directory
order is an insertion sort over FST positions; lookups are plain loops.
Signed blobs, TMDs, tickets, certs and signatures are lent as pointer
plus size; readers are filled in place from guest memory and disc
reads. Algorithm calls become plain loops and memcpy. WiiUtils keeps
only WAD install, TMD recovery and the Bluetooth getters the core uses.
A TMD view and a ticket view are written straight into the guest's
output buffer, the TMD view's size computed rather than measured off a
view built to be thrown away. A ticket, a signature and a public key are
spans into the blob they are part of instead of copies in vectors. A TMD
or ticket the guest passes to the DI view calls is read into its reader
straight from guest memory.
A ticket, TMD or certificate holds its bytes in one malloc'd buffer and
hands them out as a span. ResizeBytes() gives the room to fill, so a
ticket or TMD is read from the NAND, a disc, a WAD or guest memory
straight into its reader instead of into a std::vector that is then
moved or copied in.
Ticket, TMD and certificate chain imports take spans, and the IPC
handlers pass the guest's buffers where they lie instead of copying
them out into vectors first. A certificate is read where it lies and
only its own bytes are copied, so parsing a chain no longer copies the
rest of the chain once per certificate. Deleting a ticket closes up the
ones kept in place. The savestate layout is unchanged.
The GBA core's files open as an mGBA VFile over a libretro-common
filestream (GBAVFile.c). A read-only map of a mapped file lends the
mapping itself, so the ROM and the BIOS are read where they lie; a save
is mapped into a buffer of its own and written back when mGBA syncs it.
A ROM in an archive or a zip is read or inflated straight into the
memory file mGBA maps, with no buffer in between.
The state and save import/export, e-Reader card queueing, ROM info and
forced disconnect entry points nothing calls are gone.