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.
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.
On Vulkan and Direct3D 12, in dual core, the GPU loop runs on a thread
of its own, as the frontend interface for both lets another thread
submit: retro_run starts the field, waits for it no longer than two of
the frontend's frames (40 ms at most), hands over the newest picture
finished and returns. A shader compiled, or anything else slow on the
GPU side, holds up the GPU thread only. The loop runs in slices of 5 ms,
so a request to park or quit is seen that soon; a slice that ends a
field counts it for retro_run.
Pictures go from the GPU thread to the frontend's through a queue: the
newest published, the one being handed over and the one shown, any of
which the frontend may read. Each output draws into images of its own,
three of them: a picture published and not yet handed over is drawn
into again in place. EndFrame hands over with set_image, or
set_texture_fenced and set_texture, on the frontend's thread, and an
image is free again once the frontend's frame that read it is
submitted. An XFB copy handed on in place is kept alive by the
presenter while the output holds it, and the output puts it back in the
layout or state the frontend reads it in before every submission of
the core's, as the frontend submits on its own thread whenever its
frame is due.
Savestates, cheats and disc changes let the field in flight end on the
GPU thread and park it while the frontend's thread has the video state;
unloading and a context destroyed stop it, and the next retro_run starts
it again.
Direct3D 11 and OpenGL keep the GPU loop on the frontend's thread: the
immediate context is shared with the frontend through lock_context,
which a thread holding it through a slow compile would hold the
frontend's frame up on, and the OpenGL context is the frontend's
thread's.
retro_run runs the GPU until the CPU thread ends the field, and the
field ends only as fast as the CPU thread gets through it: a disc read
or anything else holding that thread up held the frontend's frame up
with it, and the frontend counted no frames.
The GPU loop now has a deadline on the wall clock, two of the
frontend's frames and never more than 40 ms: it is checked every 64
chunks of the fifo, and the loop's sleep waiting for work is bounded by
it. Past it the run reports a repeat and returns, the CPU thread goes on
with its field, and the next run takes the rest of the field's work.
A field's end that comes while the loop is out of time is kept for that
next run. Every run reports one frame, before the first picture too,
with the size the frontend was told.
A field swapped while a state is saved or loaded, outside retro_run,
is not presented: the frontend takes pictures only inside retro_run, so
the next field is presented instead.
Whatever pauses the CPU thread from the frontend's (savestates,
cheats, a disc change, unloading) first runs the GPU until the field in
flight ends, so the CPU thread is paused at a field's end as after a
full run, never waiting on GPU work only this thread runs.
With version 2 of the frontend interface, a picture whose draw would be
a plain copy is not drawn: the XFB texture itself is named with
set_texture and the frontend reads it where it is. That is the case
when the XFB fills the whole output from its top-left corner at the
same size, with one layer, no stereo, no colour correction and the
default resampling; with crop overscan on, a full-height XFB at the
internal resolution. Anything else is drawn into the output's targets
as before.
The immediate context orders the frontend's reads before anything later
drawn into the texture, so no sync index is waited on. Fields that
repeat the picture keep its XFB copy locked as the one being shown, and
fields that blank the screen lock it until another picture replaces it,
so the copy the frontend shows again is never written meanwhile.
The hook is FrameOutput::PresentTexture, reached through
AbstractGfx::PresentTexture: an output that only hands on its own
targets returns false and the picture is drawn.
Every retro_run hands the frontend exactly one frame. A field that only
repeats the picture before it is not drawn on any backend, OpenGL and
Direct3D on Xbox included: the frontend is told at the end of the run
that the last picture repeats (video_refresh with no data, at the last
picture's size), and shows again what it already has, the target it
reads in place or its own copy. A run that shows something new hands
it over as before, now always as a valid frame.
Loading a state no longer presents from inside retro_unserialize, where
the frontend takes no pictures. The next field presents the loaded
picture inside the next run and is never taken for a repeat of what was
shown before the load.
- CommandProcessor: the FIFO registers shared by the CPU and GPU
threads are retro_atomic words, read and written through LoadReg and
StoreReg; MMIO's LowPart/HighPart take those words.
- Fifo: the video buffer write and seen pointers, the sync ticks and
the stop request.
- PerfQueryBase: the query count and results sit behind base-class
helpers that every backend (Metal too) uses instead of touching the
atomics.
- Custom assets: the loader's memory delta and an asset's last load
time are 64-bit words; Present's swap flag is an int.
- D3D's input layout, Vulkan's waiting_for_submit.
- InputCommon: a NumericSetting's value and the Wiimote and Android
rumble levels are kept as their bits in a 64-bit word; the
ControllerInterface flags, counter and aspect ratio are int words.
- CachedBlob's fill position and stop flag.
UICommon/GameFileCache is deleted: only the Android app's game list
used it, and that is not part of the core.
Nothing is drawn over the game any more. The OSD message queue, the
ImGui overlay that drew it (with the shader-compile progress window and
the statistics, projection and scissor windows), and the imgui and implot
libraries are removed from the build.
Core::DisplayMessage now writes to the log, and the former OSD calls
go through it. Shader compilation no longer presents frames outside
retro_run while it waits.
Deleted with it: the "OSD enabled" core option, the OSD, FPS, speed,
graph, statistics and internal-resolution display settings, and the
ImGui shaders and texture conversion.
retro_run runs one video frame and the frontend paces it, so the core no
longer paces itself:
- The speed limiter is gone: CoreTiming::Throttle and its host-time
targets, VI skip, the presenter's presentation-time wait,
PrecisionTimer, PerformanceMetrics/PerformanceTracker, and the
emulation speed, precision timing, rush/smooth presentation and VI
skip settings and core options. The Dolphin IOS device reports 100%
and ignores speed limits a guest asks for.
- Common/FrameClock.c (C89) counts retro_run calls. Background threads
that acted every so many milliseconds wait for a number of frames
instead: real-Wiimote pool upkeep, scanning, write tests and
reconnect retries, USB scanning, the USB Gecko and GBA link
listeners, the BBA read thread, memory card flushing and the WC24
scheduler. Shutdown wakes every waiter.
- Waits that polled or timed out now block until signalled: dual-core
boot, PauseAndLock in the GPU loop, CPU thread jobs, shader
compilation progress, BlockingLoop and ParallelFor setup and
teardown.
- Bluetooth passthrough submits transfers straight to libusb; the
output worker and its timed sleep are deleted, HCI command timeouts
count emulated time, and blocking firmware commands wait for the
adapter's events.
- Real Wiimote reports go out when produced; delayed ones carry a
frame number.
- Common::Flag sits on retro_atomic.
- Deleted: SleepCurrentThread, YieldCPU, Common::SleepUntil,
Event::WaitFor, BlockingLoop::WaitYield, timer resolution changes,
the play-time tracker, Host_YieldToUI, FrameTiming::ThrottleFrame
leftovers and the never-used DO_WAIT_FOR_WIIMOTES mode.
CPU: the state machine drops its two mutexes and two condition
variables. The state is one atomic word, which the JIT still reads in
place; PowerDown sticks through a CAS. The CPU thread marks itself
active and then rechecks for a pause or a state change, and a pausing
thread stores its request and then waits for the flag to drop, a
Dekker pair, so neither misses the other. Waits park on eventcounts.
A step request and its Event travel as one exchanged pointer, pending
jobs are an MPSC stack run in order, and SetStepping and PauseAndLock
still run one at a time, through a claimed flag.
Core: the recursive core mutex goes. Stop moves the state with a CAS,
and PauseAndLock pairs are already serialized by the CPU's claim. Host
jobs are an MPSC stack the host thread moves, in order, into its own
queue, which keeps HostDispatchJobs re-entrant. RunOnCPUThread waits
on a Common::Event instead of a binary semaphore, and OneShotEvent is
removed.
State: the transferable shared mutex that counted save tasks is a
counter and an eventcount; the slot readers and the flush hook wait for
it to reach zero. TransferableSharedMutex is removed. WaitableFlag
parks on an eventcount.
PixelEngine: the token, its interrupt flags and the raised-event flag
are one atomic word; the video thread merges into it and schedules the
event only on the edge, the CPU thread takes the flags with a CAS.
Savestates keep the old fields.
VertexLoaderManager: the loader map is a fixed set of buckets, each a
list pushed with a CAS and read without a lock by the main and the
preprocessing thread; two threads making the same loader keep one.
AsyncShaderCompiler: the pending queue is the video thread's own. It
hands the most urgent items to two slots per worker, and finished items
come back on an MPSC stack, so neither side takes a lock.
OSD messages are posted to an MPSC stack and taken in by the video
thread when it draws; a screenshot request is one exchanged string.
Also removed, being unreachable: the surface change and resize requests
nothing sends (and the swap mutex guarding them), the ImGui keyboard
and mouse input nothing feeds (and the ImGui mutex, as only the video
thread touches the context now), ClearMessages, and the libretro Vulkan
WaitForPresentation with its mutex and condition variable.
Both fed user-written GLSL to the backends at runtime, which the move to
bytecode emitted straight from the shader generators leaves no compiler
for. The frontend's own shader pipeline covers post-processing.
The presentation pass keeps a plain copy and the built-in correction
shader for output resampling, colour space and gamma correction and HDR
scaling, now compiled in rather than loaded from Sys/Shaders. The
graphics-mod system keeps its draw, EFB, projection and texture actions;
custom pipelines, materials, shader, mesh and sampler assets, the custom
constant buffers in every backend and the two mods built on them are
gone, along with the shader generators' custom-shader hooks.
Dolphin fitted its picture into the frame by the Aspect Ratio mode, bars
and all, and the frontend scaled the frame to 4:3 or 16:9 once more, so
every mode but Stretch came out squeezed or stretched (Custom, Custom
Stretch and Raw the most). Dolphin now always stretches over the frame,
and the core gives the frontend the aspect ratio Dolphin works out for the
chosen mode (Presenter::CalculateDrawAspectRatio for a given mode), again
whenever it changes. Stretch and VR are as before.
Vulkan hands the frontend the whole swapchain image Dolphin presented
into, as D3D11 and OpenGL do with their backbuffer, instead of the EFB's
size (640x528 against a 640x480 image), which squeezed the picture.
fb4ff3e added a statistics option to show the internal resolution, but
it just showed the total size of the EFB (which is always 640x528) times
the IR scale, so it didn't convey any useful information.
This commit instead makes the option use the size of the last XFB copy
(not multiplied by the IR scale), which changes based on the game's
rendering resolution.