Integer num_format fetches now get their scale CPU-side instead of trying to guess in the shader. This is authoritative; no cvar. Both translators now use texture_integer_scale_bits after signs/gamma and before exponent bias. This alone fixes black screens and a bunch of rendering bugs across at least several dozen titles.
This new information lives in the updated FormatInfo table, including fixed component widths.
Resolve also has two new fixes.
8_8_8_8_GAMMA EDRAM sources can now decode through the PWL curve while still in linear space, before MSAA resolve or format conversion, then re-encoded for gamma destinations. This is also now default enabled via gamma_decode_pwl_resolve and has improved blowout in at least 4 titles, with no obvious regressions thus far.
Full resolve can also now pack fixed destinations according to copy_dest_number.
For 4xMSAA 32bpp, samples 2/3 add a +4 byte horizontal offset to the
EDRAM address. The old typed buffer code (Buffer<uint4>) implicitly
truncated this offset via integer division (>> 2), and the .yw swizzle
compensated by selecting odd elements within the aligned uint4 load.
After the ByteAddressBuffer conversion, Load4 uses the exact byte
address including the +4 offset, shifting the load window by one dword.
The .yw swizzle then picks samples from the wrong pixels, causing
visual artifacts like vertical streaks in Resident Evil 5.
Fix by aligning the load address down to 16 bytes and selecting the
swizzle based on whether the address was offset.
Adds async_shader_compilation cvar (default true) that forces new
pipelines to be created async from the main thread, skips draws entirely
on D3D12 and on vulkan replaces pixel shader with placeholder until the
real one is ready. Causes some visual artifacts on first load but
greatly reduces load times and stutter.
The original commit (2eea146b) correctly added kXenosFormat_16_16_EDRAM
case labels and fixed the unpack clamp from -1.0 to -32.0, but
incorrectly changed the resolve pack functions from UNorm to signed EDRAM
encoding. The resolve destination is a regular texture sampled as UNorm
[0,1], not EDRAM. Using EDRAM packing double-encodes the [-32,32]
scaling, crushing all values by ~64x and caused visual issues
particularly in UE3 titles.
The bytecode submitted with the change was actually correct, but the
source had additional incorrect pieces that only manifested when shaders
were rebuilt.
Replaces the UNorm packing functions with custom Edram functions to preserve the native [-32.0, 32.0] float range and scale mathematically instead of using saturate(). Fixes displaced water meshes in games like Mercenaries 2.
Drivers and hardware may provide more optimal paths for untyped buffers
compared to typed.
Also, ByteAddressBuffer may be bound via a root descriptor in Direct3D 12,
greatly simplifying the binding logic.
ByteAddressBuffer also doesn't have the 128 * 2^20 element count limit that
typed and structured buffers have, and doesn't require the structure stride
to be specified at resource creation time in Direct3D 11.
With render target HLE, directly store linear values as R16G16B16A16_UNORM
without gamma conversion, as this format provides more than enough bits
(need at least 11 per component due to the maximum scale being 2^3 in the
piecewise linear gamma curve) to represent linear values without precision
loss.
This makes blending work correctly in linear space, improving quality of
transparency, lighting passes, and fixing issues such as transparent parts
of impact and footstep decals in 4D5307E6 being bright instead.
The new behavior is enabled by default, as it hugely improves the accuracy
of emulation of this format, that is pretty commonplace in Xbox 360 games,
with likely just a small GPU memory and bandwidth usage increase, compared
to the alternatives that were previously available on the HLE RB path.
It's currently implemented only on Direct3D 12, as most of the current GPU
emulation code is planned to be phased out and redone, and no methods other
than 8-bit with pre-conversion were implemented on Vulkan previously.
To implement on Vulkan later, same conversion as in the Direct3D 12
implementation will need to be done in ownership transfer and resolve
shaders. Currently it's somewhat inconvenient to decouple the conversion
functions in `SpirvShaderTranslator` from an instance of the translator due
to vector constant usage. Later, simpler SPIR-V generation functions may be
added (`spv::Builder` usage in general is overly verbose).
The previously default method (8-bit storage with pre-conversion in shaders
and incorrect blending) can be re-enabled by setting the
"gamma_render_target_as_unorm16" configuration option to `false`. This may
be useful if the game, for instance, switches between 8_8_8_8_GAMMA and
8_8_8_8 formats for the same data frequently, as switching will result in
EDRAM range ownership transfer data copying now. Also, the old path is
preserved for Vulkan devices not supporting R16G16B16A16_UNORM with
blending.
The other workaround that was available previously, replacing the PWL
encoding with host hardware sRGB with linear-space blending in render
target management and in texture fetching, was also inherently inaccurate
in many ways (especially when games have their own PWL encoding math, like
4541080F that displayed incorrect colors on the loading screen), and
required tracking of the encoding needed for ranges in the memory.
The sRGB workaround therefore was deleted in this commit, greatly
simplifying the code in the parts of render target, texture and memory
management and shader generation that were involved in it.
The 32_32_32_FLOAT format seems to be vertex-only, so it looks like there
can't be storage elements smaller than a single texel.
So, use a more precise name that can't be confused with "picture element"
(pixel) or "texture element" (texel) that represents a single logical pixel
rather than a storage block of pixels.
Switch between even and odd 16-byte element sequences along X by simply
flipping a bit rather than going to a different resolution-scaled group of
pixels, by increasing the size of the group within the constraints imposed
by tiling.
Use the _xe suffix instead of the xesl_ prefix for quicker visual
recognition of identifiers, also switch to snake_case for consistency.
Also add the f suffix to float32 literals because the Metal Shading
Language is based on C++.
For more optimal usage of exports and the parameter cache on the host regardless of how effective the optimizations in the host GPU driver are. Also reserve space for Vulkan/Metal/D3D11-specific HostVertexShaderTypes to use one more bit for the host vertex shader type in the shader modification bits, so that won't have to be done in the future as that would require invalidating shader storages (which are invalidated by this commit) again.
Ordering the descriptor sets by the change frequency on Vulkan, in increasing order (the opposite of D3D12 root signatures). The EDRAM binding never changes there (always one storage buffer), while the destination buffer binding may become changeable in the future (to split dispatches if exceeding `maxStorageBufferRange`, for example).
While the alpha of the texture data is not used at all (replaced with blue using the view swizzle), still make the shader code state the intention more explicitly if the format is decompressed for use as signed. Unsigned 1.0 is 0xFF, while signed 1.0 is 0x7F.