* video_core: respect SPI_BARYC_CNTL front-face encoding
- Read FRONT_FACE_ALL_BITS from SPI_BARYC_CNTL
- Emit +1.0/-1.0 when sign encoding is selected
- Emit integer 1/0 when all-bits encoding is selected
- Carry the register state through FragmentRuntimeInfo
- Add a boolean IR attribute path for FrontFacing
- Include the mode in shader specialization and cache metadata
- Add regression tests covering both AMD-defined encodings
Fixes according Roamic, Raphael, and IndecisiveTurtle recommendations.
* shader_recompiler: Remove unused IsFrontFace attribute and enhance front face handling in translation, rebase
* shader_recompiler: Fix front-face test compilation
- Use the current hw.fs runtime-info layout for front-face encoding.
- Update the regression helper to the HwStage and SwStage APIs.
- Address test compilation feedback from @raphaelthegreat
---------
Co-authored-by: w1naenator <valdis.bogdans@hotmail.com>
Co-authored-by: TheTurtle <47210458+raphaelthegreat@users.noreply.github.com>
GCN has the concept of hardware and software stages. Software stages are the shader stages exposed in graphics APIs like opengl/vulkan, and each software stage runs on a hardware stage.
Depending on how many and what software stages are used in a graphics pipeline, the hardware stage a software one is executed on can change, so the mapping is not fixed.
The following table from ACO describes the various combinations for HW-SW stages: https://gitlab.freedesktop.org/mesa/mesa/-/blob/c90ae5f7/src/amd/compiler/README.md#gfx6-8
Certain software stages like the vertex and tess eval shader can run a lot of different hardware stages, which creates problems because runtime info is a tagged union on HW stage.
Current main uses struct inheritance to make it work, but that will get clunky when more data is added to other stages. In general there is a lack of separation between what data
is required when a HW stage is used and what for SW stage.
An example is the tess_emulated_primitive variable. This is used for skipping exports in a software vertex shader when tessellation stages have been inserted after it
for Quad/RectList emulation, because it is undefined behavior to not export them from the last geometry stage. Having this data field in the HW vertex info is not semantically
correct because HW vertex is always the last geometry stage that runs. So this only works when there isnt any HW geometry shader active, as the HW vertex shader is not normally processed in that case.
Another example is shader specialization previously checked the HW vertex stage to specialize the fetch shader. This only works on simple VS+PS pipelines when HW VS runs the SW VS.
Fetch shader is part of the SW VS state, its run on first geometry stage not the last, and this issue got exposed because of the new state separation.
This commit refactors runtime info to contain two tagged unions, one from SW stage and the other HW stage. This makes it obvious what is the scope of each state.
When recompiler does a switch on sw stage, it becomes more apparent it should only access sw state members unless it also checks which hw stage it runs on, which will result in less bugs
The code regarding structurization has not changed much since the very first commit of the recompiler.
The algorithm has proven very reliable in practice however it doesnt produce optimal code.
More specifically the algorithm does not know to emit if-else statements, it can only emit ifs
The following code
```
a = 0
if (cond) goto L1
a = 1
goto L2
L1:
use(a)
L2
```
will emit the following
```
if (cond) %0 = 1
else %1 = 0
%3 = phi(%0, %1)
if (!cond) use(%3)
```
if the structurizer knew about else statement it could have emited much more optimal code
```
%0 = 0
if (cond)
%1 = 1
else
use(%0)
```
In general while suboptimal, this is harmless. It does matter though for one specific case; K$ pointer chasing
if "a" were a resource descriptor load, then the program ends up with a phi node with 2 possible sources which results in ambiguity.
The obvious solution is to improve the structurizer codegen to reduce these semantically redundant phis.
Relying on structurizer quality for correctness is generally fragile however.
On main this issue is solved by performing dominance and reachability analysis on the original unstructured CFG.
The phi tree is scanned and all possible sources are collected. Then sources are examined in pairs (a, b), if b dominates a, then a can be removed
The goal of this PR is to solve the same issues but in a more fundemental way; most IR passes do not care about structurization
and some like readlane elimination that duplicate phis also benefit from unstructured graphs. So defer structurization until the end of compilation
The new pipeline builds an unstructured graph of IR blocks and runs SSA rewrite on those. The SSA pass has been reworked again to remove
recursion and not rely on def lookups during phi trivialization. This allows removing Opcode::Identity, its removal pass and all special checks for it
in various places inside the recompiler.
When optimization passes are done, phis are lowered back to hardware registers. This is possible with the new RegTag structure that intrusively
stores register information for every phi. This used to be stored with a hash map inside ssa pass, but having this information later in the pipeline is of benefit
After structurization the ssa is repaired to fixup any dominance violations that have resulted and ssa rewrite runs again to resolve added goto variables,
conditions and bring lowered phis back to ssa form
changes
remove identity op
remove some heap allocs
cleanup and split headers
repair ssa after structurization
faster phi lowering
use more ir builder
fix
The ssa rewrite pass is based on the spirv-opt pass of the same name:
https://github.com/KhronosGroup/SPIRV-Tools/blob/main/source/opt/ssa_rewrite_pass.cpp
This commit brings the pass closer to that reference implementation.
Notably this fixes applying the pass to unstructured graphs which seems to be broken otherwise
Additionally the manual stack iteration of ReadVariable has been replaced with recusion
which is much easier to reason about
Undef handling has also been changed, before each block would define its own Undef instruction,
now the pass will keep track and reuse compatible instructions add insert them to the first block