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.
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.
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.
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.
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.
The geometry shader generator and the layered pass-through geometry
stage now write GLSL for Direct3D too, as for OpenGL and Vulkan, and the
vendored rslc compiles them to DXBC like the other stages. Direct3D
binds the geometry block at b0.
With every stage compiled by rslc, the runtime compiler is no longer
used: d3dcompiler_47.dll is not loaded, D3DCompile and its target table
are gone, and the core no longer needs the DLL on the system.
GenerateVSOutputMembers loses its Direct3D geometry-stage case and its
HLSL semantics, which only the HLSL generators used.
Yellow squiggly lines begone!
Done automatically on .cpp files through `run-clang-tidy`, with manual corrections to the mistakes.
If an import is directly used, but is technically unnecessary since it's recursively imported by something else, it is *not* removed.
The tool doesn't touch .h files, so I did some of them by hand while fixing errors due to old recursive imports.
Not everything is removed, but the cleanup should be substantial enough.
Because this done on Linux, code that isn't used on it is mostly untouched.
(Hopefully no open PR is depending on these imports...)
The HLSL compiler incorrectly decides isnan can't be true, so this
workaround was originally added in 52c82733 but lost during the
conversion to SPIR-V.
Otherwise, texelFetch() will use an out-of-bounds layer for game textures (that have 1 layer; EFB copies have 2 layers in stereoscopic 3D mode), which is undefined behavior (often resulting in a black image). The fast texture sampling path uses texture(), which always clamps (see https://www.khronos.org/opengl/wiki/Array_Texture#Access_in_shaders), so it was unaffected by this difference.
Specifically, when using Manual Texture Sampling, if textures sizes don't match the size the game specifies, things previously broke. That can happen with custom textures, and also with scaled EFB copies at non-native IRs. It breaks most obviously by not scaling the texture coordinates (so only part of the texture shows up), but the hardware wrapping functionality also assumes texture sizes are a power of 2 (or else it will behave weirdly in a way that matches how hardware behaves weirdly). The fix is to provide alternative texture wrapping logic when custom texture sizes are possible.
This adjusts the NaN replacement logic introduced in #9928 to work around the HLSL compiler optimizing away calls to isnan, which caused that functionality to not work with ubershaders on D3D11 and D3D12 (it did work with specialized shaders, despite a warning being logged for both; that warning is also now gone). Note that the `D3DCOMPILE_IEEE_STRICTNESS` flag did not solve this issue, despite the warning suggesting that it might.
Suggested by @kayru and @jamiehayes.
SPDX standardizes how source code conveys its copyright and licensing
information. See https://spdx.github.io/spdx-spec/1-rationale/ . SPDX
tags are adopted in many large projects, including things like the Linux
kernel.
Now that we've converted all of the shader generators over to using fmt,
we can drop the old Write() member function and perform a rename
operation on the WriteFmt() to turn it into the new Write() function.
All changes within this are the removal of a <cstdarg> header, since the
previous printf-based Write() required it, and renaming. No functional
changes are made at all.
These are only ever used with ShaderCode instances and nothing else.
Given that, we can convert these helper functions to expect that type of
object as an argument and remove the need for templates, improving
compiler throughput a marginal amount, as the template instantiation
process doesn't need to be performed.
We can also move the definitions of these functions into the cpp file,
which allows us to remove a few inclusions from the ShaderGenCommon
header. This uncovered a few instances of indirect inclusions being
relied upon in other source files.
One other benefit is this allows changes to be made to the definitions
of the functions without needing to recompile all translation units that
make use of these functions, making change testing a little quicker.
Moving the definitions into the cpp file also allows us to completely
hide DefineOutputMember() from external view, given it's only ever used
inside of GenerateVSOutputMembers().
Migrates most of VideoCommon over to using fmt, with the exception being
the shader generator code. The shader generators are quite large and
have more corner cases to deal with in terms of conversion (shaders have
braces in them, so we need to make sure to escape them).
Because of the large amount of code that would need to be converted, the
conversion of VideoCommon will be in two parts:
- This change (which converts over the general case string formatting),
- A follow up change that will specifically deal with converting over
the shader generators.