A generator can now build its stage through rslc's builder instead of
writing GLSL for the backend to parse: AbstractGfx::CreateShaderFromBuild
takes a build function, and each backend asks rslc for its own
destination from the built stage - SPIR-V for Vulkan, DXBC at the
device's feature level for D3D11 and D3D12, GLSL or GLSL ES at the
context's version for OpenGL, MSL for Metal. VideoCommon/ShaderBuild
carries the per-backend resource layout the shader headers' macros gave,
and builds a stage in the worker's shader arena, again from the start
where the arena has to grow.
The framebuffer utility stages move first: the screen quad, texture
copy, clear and EFB poke vertex stages, the passthrough geometry stages,
and the texture copy, color and multisample resolve pixel stages. Each
compiles to the same SPIR-V and DXBC words as its GLSL did; the text
writers are gone.
rslc prints OpenGL's GLSL without bindings, so the OpenGL backend binds
blocks, samplers, attributes and outputs by name for every program.
Nothing made a framebuffer with additional colour attachments, so they
go from AbstractFramebuffer, CreateFramebuffer and every backend, and
FramebufferState's additional_color_attachment_count (always 0) with
them: render passes, views, clears, RTVs and blending are for one
colour attachment, in fixed arrays rather than vectors.
Vulkan samplers are kept in an open-addressed table grown by doubling
and render passes in a fixed array, so looking one up never allocates.
Play allocations on the core's thread: 62 to 58.
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.
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.
The software and null renderers run without a hardware context. They
come up at the end of retro_load_game, where a frontend with a context
would call context_reset, and go down with the rest at unload.
The software renderer's picture goes to video_refresh straight from its
XFB texture, in one pass into XRGB8888 order, into a buffer that grows
only with the picture: no GL texture upload, no draw, no window of its
own. The GPU thread's g_gfx is the backend's own from the start.
Nothing ever made one: every backend was handed a null includer, and the
OpenGL backend's #include resolver returned the source untouched. The
parameter goes from CreateShaderFromSource and everything beneath it,
and ShaderCompileUtils with it.
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.
Remove the [[maybe_unused]] annotation from various parameters that are
unconditionally unused and comment out their names instead. This makes
it unambiguous that the variables are unused, while making the remaining
[[maybe_unused]] annotations more reliable indicators that those
variables are in fact used in some contexts.
These parameters are mostly in overridden functions where the override
doesn't need that particular variable.
Fixes a build failure with clang 17.
The destructor needs to be in the cpp file, since we have a forward
declared std::unique_ptr type as part of the class. So technically the
default inline destructor could invoke without seeing the full data type
definition.
Before, it used a fallback where it returned a default object, where the width and height were set to 0. Presenter::Initialize() used GetSurfaceInfo to set the backbuffer size, then used that size when initializing the on-screen UI (even for the software renderer, where the on-screen UI isn't currently present), which meant that ImGui got a window size of 0 and thus resulted in a failed assertion.
Although BindBackbuffer checks for size changes, it doesn't help because ImGui has already been initialized, and the size hasn't actually changed since initialization occured.
Fixes one aspect of https://bugs.dolphin-emu.org/issues/13172.