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.
With GPU texture decoding, the decoding dispatch and the copy into the
texture are recorded in the init command buffer, which runs before the
draws, instead of between them: decoding a texture no longer ends the
render pass. The scratch texture is used there alone, and a decoded
texture is new or from the pool, which this command buffer's draws do
not use, so the layouts each tracks still hold. Other backends record it
in order, as before.
The stutter gate counts a frame as differing only when the pictures it
was presented as have none in common between the two runs.
Where the backend sets cull mode, depth, blending and the logic op per
draw (BackendInfo::uDrawState*Bits), ubershader pipelines are kept under
the uid as the driver bugs leave it, without that state and without the
pixel shader bits the shader does not read. The vertex manager hands
the draw's state to the backend with AbstractGfx::SetDrawState. On
Vulkan, every state a draw is in finds the pipeline built before the
game started.
make STATS=1 builds parallel_gx1_libretro_stats with counters for each
kind of draw-path compile, wait and cache write, worker compiles, and
every shader and pipeline made; release builds have none. Tools/stutter
generates a synthetic FIFO corpus, runs it in the four cache states,
times parent and candidate ABBA, compares pictures, and gates the
counters against baseline.txt and the parent.
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.
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.
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.