123 Commits
Author SHA1 Message Date
LibretroAdmin 90e4d677a9 Video: build the EFB copy encoders by calls
The encoder an EFB copy to RAM runs is built with rslc's builder instead
of printed as text and parsed again. Built and printed stages compile to
the same SPIR-V and DXBC words for each of the 14 copy formats, with and
without geometry shaders and a reversed depth range; for OpenGL, the
same 56 at GLSL 4.50, 3.30, ES 3.10 and ES 3.20 compile in
glslangValidator. The text encoder goes.
2026-11-10 18:02:10 +01:00
LibretroAdmin cab5004132 Video: build the pixel ubershaders by calls
The pixel ubershader is built with rslc's builder instead of printed as
text and parsed again, on every backend. Built and printed stages
compile to the same SPIR-V and DXBC words: each of the 108 uids with 30
random host configs, on Vulkan with and without subgroup reduction and
on D3D. For OpenGL, where framebuffer fetch blends and logic-ops in the
shader, each stage a GLSL 4.50, 3.30, ES 3.10 or ES 3.20 context can be
asked for compiles in glslangValidator.

Both pixel generators now declare their inputs in one place. The text
the ubershader was the last user of goes: the pixel shaders' common
header, the switch trees, the lighting function, the bitfieldExtract
header and the VS_OUTPUT members and their interpolation qualifiers.
2026-11-10 17:46:20 +01:00
LibretroAdmin 7b0fdb6702 Video: build OpenGL's pixel shaders by calls
OpenGL's pixel shader is built with rslc's builder as the other
backends' are, and the text generator goes. rslc is told what the
context's header said: framebuffer fetch by the EXT or ARM extension,
conservative depth in place of early fragment tests, and subgroup
reductions.

Built for random pixel uids against GLSL 4.50 with subgroups, 3.30 with
conservative depth, and ES 3.10 and 3.20 with either framebuffer fetch,
each stage these contexts can be asked for compiles in glslangValidator.
2026-11-10 17:04:25 +01:00
LibretroAdmin 2d2b89ea7c Video: build the pixel shaders by calls
The pixel shader is built with rslc's builder instead of printed as text
and parsed again, on Vulkan, D3D11, D3D12 and Metal. Built and printed
stages compile to the same SPIR-V and DXBC words: 15,619 random pixel
uids and host configs, with and without subgroup reduction, and every
uid the text refuses the builder refuses too.

The builder's flags say what the text's header said of the device:
subgroup operations, an inverted gl_HelperInvocation and subgroup
operations broken by discard. Vulkan and Metal pass them; Metal builds
at MSL 2.3, as its text compiles.

OpenGL still prints the stage as text until rslc prints framebuffer
fetch, conservative depth and subgroups in GLSL.
2026-11-10 16:04:08 +01:00
LibretroAdmin f2f126da96 Video: build the vertex ubershaders by calls
The vertex ubershader is built through rslc's builder, as the
specialized vertex shader already is, so no GLSL is generated or
parsed for it on the compile workers.

The lighting function and the per-channel vertex lighting it shares
with the pixel ubershader, and bitfieldExtract for backends without it,
are builder pieces of their own. The text writers nothing uses any
more go: the vertex ubershader's, the line and point expansion, the
line offset, dolphin_isnan's and the output member assignment.

Metal's built shaders now get the same MSL header and fixups its
parsed ones do, so the ubershader lighting loop stays unrolled.

Built stages compile to the same words the text did for Vulkan and
D3D for every texgen count across random host configurations.
2026-11-10 08:19:30 +01:00
LibretroAdmin 1396da6572 Video: build the vertex shaders by calls
The specialized vertex shader is built through rslc's builder instead
of written as GLSL for each backend to parse back: Vulkan gets SPIR-V,
D3D its bytecode and OpenGL GLSL straight from the built stage, with no
text generated or parsed on the compile workers.

The pieces the pixel shaders and the ubershaders will build from too
are shared: the vertex uniforms block, dolphin_isnan, the line and
point expansion and the lighting functions. The text generator and the
lighting macros nothing used are gone.

Built stages compile to the same words the text did for Vulkan and
D3D across random uids and host configurations.
2026-11-10 07:48:29 +01:00
LibretroAdmin 4a43bd75ec Video: build the geometry shaders by calls
The geometry stage moves to the builder, on pieces the vertex and pixel
stages will share: VS_OUTPUT's layout and member copy, struct Light, the
geometry stage's uniforms and the line offset. Every primitive, texgen
count and host configuration compiles to the same SPIR-V and DXBC words
as its GLSL did, but one: under MSAA the GLSL put 'centroid in' inside
the VertexData blocks where the backend reports no binding layout, as
Vulkan and Direct3D 12 do, and rslc refuses that, so no geometry stage
compiled with MSAA there. The built stage marks those members centroid
(or sample) and compiles to what the GLSL with plain 'centroid' would.
2026-11-10 05:58:14 +01:00
LibretroAdmin 8fcba45939 Video: build the EFB copy shaders by calls
The EFB copy to VRAM stages move to the builder: the copy vertex stage,
and the copy pixel stage for every format and for the one that reads
the format from copy_flags, with the stereo mono-depth read and the
depth range the backend has. Each compiles to the same SPIR-V and DXBC
words as its GLSL did; TextureConverterShaderGen writes no GLSL now.
2026-11-10 05:26:27 +01:00
LibretroAdmin 0cb6ba6596 Video: build the EFB conversion, reinterpret and restore shaders by calls
The rest of the framebuffer stages move to the builder: the EFB format
conversion pixel stages (every conversion, with and without multisampling
and sample shading), the texture reinterpret stages between the formats
there is one for, and the EFB restore stage. Each compiles to the same
SPIR-V and DXBC words as its GLSL did; a pair of formats with no
reinterpret stage builds none, as before. FramebufferShaderGen writes no
GLSL now.
2026-11-10 05:21:51 +01:00
LibretroAdmin 8000d06bf1 Video: build the framebuffer utility shaders by calls
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.
2026-11-10 05:03:08 +01:00
LibretroAdmin 8a022edefc Remove the video statistics nothing reads
Every Statistics counter, the projection copies and the widescreen ratio
averages were only written since the debug UI went; the counters bumped
per primitive, per triangle and per pixel in the software rasterizer.
The widescreen heuristic keeps only the two vertex counts it compares,
from one projection/viewport ratio per flush instead of up to three.
GetCPRegInfo, the CP register describer, has no caller.
2026-11-09 20:17:05 +01:00
LibretroAdmin 01c993b7bd EFB copies to RAM: one encoder per format, built before the game
An encoder reads the rest of the copy (the EFB's format, depth, YUV,
filter rows, overflow, gamma) from its uniforms, in what was the
block's padding, so one per copy format draws them all. Where copies
to RAM may be made, every format's is built on the workers before the
game starts: a copy to RAM has no other pipeline to draw with. The
gate checks pictures with copies to RAM too, and fails one that waits
for its encoder; the corpus made the parent build 201 on the draw path.
2026-11-09 04:42:40 +01:00
LibretroAdmin a4cc219a82 Ubershader pipelines sized for every texgen until specialized
In Async (UberShaders), where ubershaders draw only until the
specialized pipeline is built, their pipelines are keyed sized for 8
texgens, and the pixel shader reads the draw's count from XF's, kept
in the pixel constants' padding (the block and savestates keep their
size). 3 are built before the game starts where there were 27. Over
the corpus, shaders made fall from 134 to 102, their words from 737739
to 274571, pipelines from 182 to 158, and loading from about 1.03 s to
0.22 s on lavapipe, with the same pictures; ubershaders sized so drew
every frame the same too. Synchronous (UberShaders) keeps them sized
for the draw's texgens.
2026-11-09 03:56:54 +01:00
LibretroAdmin 93a2b5c122 Shader names borrowed, not formatted or kept
A shader's name labels it for debugging tools as it is made and is not
kept: Vulkan terminates it in a stack buffer, GL and D3D11 pass it with
its length, D3D12 widens it for a compute pipeline alone. The shader
cache names its shaders with literals instead of formatting each uid
on the heap, on the workers.
2026-11-09 03:05:30 +01:00
LibretroAdmin 2495414cab Stutter stats: what generating and emitting shaders costs
The stats build counts the cycles generating GX shader text and in
rslc, and the words rslc emits. The gate holds the words against the
baseline and the parent, and reports each state's cycles in text, rslc,
the workers and the driver.
2026-11-09 03:05:26 +01:00
LibretroAdmin a14f48b29c Specialized pipelines keyed without the state set per draw
Where the backend sets cull mode, depth, blending and the logic op per
draw, specialized pipelines are kept under the uid as the driver bugs
leave it, without that state and the pixel shader bits the shader does
not read, as ubershader pipelines are; the draws hand their state to the
backend with SetDrawState. The uid cache still records the uids the
draws asked for. Over the corpus, pipelines made fall from 620 to 236,
one per driver pipeline.
2026-11-09 01:19:32 +01:00
LibretroAdmin 82d383de6e Shader cache binaries written from the shaders, not copied
With the shader cache on, a shader cache file borrows each new shader's
binary from the shader that owns it (LinearDiskCache::AppendBorrowed)
and writes it from there as it closes, which it does before the shaders
go. A binary is no longer copied into the cache's own buffer.
2026-11-09 00:38:35 +01:00
LibretroAdmin b057341332 Pipeline cache data written by the driver where the cache keeps it
A disk cache makes room for an entry at the end of what it will write
(LinearDiskCache::AppendBegin), and the backend writes its pipeline data
straight there: GL's program binary from glGetProgramBinary, D3D12's
blob copied once out of the driver's. The Vulkan pipeline cache is built
in place too, vkGetPipelineCacheData writing into the store written to
disk. No vector in between, no copy of it.
2026-11-09 00:15:13 +01:00
LibretroAdmin d494faf137 Texture reinterpretation and decoding built with the shared pipelines
The eight format pairs the texture cache reinterprets on the GPU are
built with the shared pipelines, and with GPU texture decoding every
decoding shader, so a texture read in another format, or decoded, never
waits for a compile. The stutter corpus reads some of its copies back in
each pair's other format.
2026-11-08 21:39:52 +01:00
LibretroAdmin 8c32522896 Shader cache off by default, its writes kept until it closes
A "Shader Cache" core option, off by default: nothing needs it to play
without stutter, and machines slow to compile shaders can turn it on to
start games sooner. With it on, a cache file keeps what it is handed in
memory and writes it in one write as it closes, so a draw that builds a
shader or pipeline no longer writes to the disk.

The stutter gate runs cold with the cache off, and fills it with a run
before the recipes and warm runs, which have it on.
2026-11-08 21:10:33 +01:00
LibretroAdmin 4e90104ba0 EFB copies to VRAM drawn by a pipeline per format, never built on a draw
The EFB copy pixel shader reads the copy's filter rows, overflow,
gamma, alpha, depth and intensity from its uniforms, each branch taking
the path a shader built for that copy would, so one is built per
format. One more, built with the shared pipelines, reads the format
from them as well and draws a format's copies until the format's own is
built on a worker.

The stutter corpus copies the EFB to a texture in every colour,
intensity and depth format, with the EFB with and without alpha, draws
it back textured, and cycles the copy filter and gamma. Each frame
draws its number, which the harness reads back, so the picture check
compares the two cores frame by frame in both synchronous modes. The
baseline is the new corpus's.
2026-11-08 20:39:57 +01:00
LibretroAdmin 73733abf24 Ubershader pipelines keyed without the state set per draw
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.
2026-11-08 19:10:11 +01:00
LibretroAdmin 5bd6734ce3 Ubershader pipelines built before the game starts
With ubershaders, every ubershader pipeline there can be is built
before the game starts, on the precompile threads, so a draw never
waits for the driver to compile one. A reload waits for them again.
2026-11-08 18:26:41 +01:00
LibretroAdmin fa3c5e69bf Vulkan: logic op set per draw, ubershader logic ops precompiled
Where the device sets the logic op per draw, its mode is no longer part
of a pipeline, so one ubershader pipeline with integer output draws
every mode. The precompile also builds the logic op pipelines with
dual-source output asked for, as BlendingState::Generate leaves it.
The stats build counts the pipelines the driver compiles or links, and
those a draw waits for.
2026-11-08 17:28:30 +01:00
LibretroAdmin b08783a161 Stutter harness: stats build, corpus, gate and Vulkan baseline
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.
2026-11-08 17:01:39 +01:00
LibretroAdmin 85b64dc7b0 VideoCommon: video globals and backend objects as plain pointers 2026-11-08 03:21:40 +01:00
LibretroAdmin 6f709e6686 VideoCommon: shaders and pipelines as plain pointers 2026-11-08 03:19:11 +01:00
LibretroAdmin 080e27d6de VideoCommon: pending pipeline uids in plain arrays 2026-11-08 02:19:56 +01:00
LibretroAdmin e358af975f VideoCommon: EFB copy and texture conversion pipelines in uid tables 2026-11-08 02:19:52 +01:00
LibretroAdmin e271683d69 VideoCommon: shader and pipeline caches in open-addressed uid tables
A draw that changes its pipeline finds it with one hash of the uid and a
compare, where a map walked down comparing whole uids. The tables, in C,
hold each uid's shader or pipeline and whether it is compiling, and the
shader cache deletes what they hold.
2026-11-08 02:05:01 +01:00
LibretroAdmin a108f60c5e VideoCommon: shader compiles queue pooled items without allocating
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.
2026-11-07 22:59:17 +01:00
LibretroAdmin 52b5d83816 VideoCommon: shaders compile from the generated text without copying it
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.
2026-11-07 21:59:49 +01:00
LibretroAdmin 183b8d7ce6 VideoCommon: shader generators write through a C89 text builder
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.
2026-11-07 21:48:29 +01:00
LibretroAdmin 77ba38f542 VideoCommon: shaders keep rslc's result block for their code
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.
2026-11-07 21:11:31 +01:00
libretroadmin c878c0d818 remove LIBRETRO ifdefs 2026-11-07 14:23:51 +01:00
LibretroAdmin bc09a84dd1 GL: programs compiled on the driver's threads, drawn with once linked
Where the driver compiles on threads of its own
(GL_KHR_parallel_shader_compile or the ARB one), the asynchronous
shader modes on OpenGL compile in the background on the frontend's
context alone: a missing program's compile and link are started, a
couple of work items a frame, and its draws use the ubershaders, or
are skipped, until GL_COMPLETION_STATUS says the link is done; its
link is checked, its variables set and its binary written to the cache
then. A program drawn with before it is done is waited for there. A
software rasterizer, which compiles when it first draws, compiles on
the spot as before.

The shader compiler gets a deferred mode for it, without threads,
compiling the items waiting a few at a time, and AbstractPipeline tells
whether a pipeline's compile is still running and whether it can be
drawn with. With the driver's threads alone, every ubershader pipeline
there can be is not built in the background.
2026-11-07 02:05:36 +01:00
LibretroAdmin e4bac5f75c Shaders: ubershaders by default, seeded per game
Shader compilation defaults to Async (UberShaders): a draw whose own
pipeline is not built yet is drawn with an ubershader one, exact
rendering, while its own builds on the compiler threads, which now
number as many as the machine has to spare (cores - 3, 1 to 4) instead
of one.

What is left to stutter is an ubershader pipeline itself, built on the
spot the first time a draw needs one: one per vertex layout and fixed
function state. Every ubershader pipeline a draw uses is now recorded,
in <game id>.uberuidcache next to the specialized UID cache, and those
are built before the game starts the next time it boots; every other
ubershader pipeline there can be, and the specialized pipelines known,
build in the background while it runs instead of before it starts,
which took 21 s on lavapipe. The record is written when the cache
closes, into room reserved when it opens, so recording allocates
nothing in the middle of a game.

Without compiler threads (OpenGL here, with no shared context for
them), asking for a pipeline in the background builds it on the spot
and an ubershader one besides: the asynchronous modes draw as
Synchronous does then, and nothing extra is built before the game
starts.

Booting a FIFO log no longer hangs: the CPU thread clears the EFB, which
waits on the GPU, before it reaches its run loop, and the frontend's
thread waited for that run loop without running the GPU. It runs the
GPU until then.
2026-11-06 23:07:17 +01:00
LibretroAdmin 05a95eb519 VideoCommon, backends: remove the shader includer
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.
2026-11-06 04:34:32 +01:00
LibretroAdmin a2404c6e43 Remove the on-screen messages and the ImGui overlay
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.
2026-11-05 23:34:25 +01:00
LibretroAdmin 472dc5ae3f Remove user post-processing shaders and graphics-mod custom shaders
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.
2026-11-05 15:57:56 +01:00
RTA 9b56760080 Restored original comments 2026-09-24 19:46:18 +02:00
RTA ffbadf1904 Defer libretro pipeline UID cache writes
Queue newly discovered GX pipeline UIDs during rendering and append them when the cache closes on reload or shutdown. Keep the existing immediate writes for standalone Dolphin.
2026-09-24 01:11:11 +02:00
Martino Fontana 95dec13203 Improve usage of std::move and const references parameters
Accomplished using `run-clang-tidy` with `performance-move-const-arg,performance-unnecessary-value-param,modernize-pass-by-value`.

Changed arguments to const references, removed them where inappropriate (e.g. sink parameters). Same with std::move.

Manually reviewed each change to make sure that it makes sense, and do something more appropriate if possible.
2026-04-17 12:39:46 +02:00
JMC47 0a3aac6d85 Merge pull request #14043 from iwubcode/custom_resource
VideoCommon: separate the concept of a 'resource' from an 'asset', add Material/Shader loading
2025-12-22 17:08:02 -05:00
iwubcode 8016e2cfbd VideoCommon: move ApplyDriverBugs for the normal pipeline out into a utility function, add a way to hash the pipeline (using the vertex declaration instead of the native vertex format) 2025-11-23 11:04:24 -06:00
iwubcode 75c66e35c6 VideoCommon: add method to async shader compiler to clear pending/completed work (used on shutdown), this will in turn clear up any resources that the worker items may have held onto 2025-11-21 01:17:45 -06:00
Jordan Woyak da6c65bf3b Common: Remove the string parameters from the HookableEvent interface. 2025-11-06 22:34:40 -06:00
Jordan WoyakandDentomologist f289b06e0d Common: Make HookableEvent use non-static data.
Co-authored-by: Dentomologist <[email protected]>
2025-11-02 17:30:43 -06:00
iwubcode 96fe6a1575 VideoBackends / VideoCommon: add support for specifying include files in shader code 2025-10-26 22:47:23 -05:00
iwubcode 14459bcc1b VideoBackends / VideoCommon: rename member variables in RenderState to be consistent 2025-08-23 13:50:22 -05:00