Treat k_8 + LOW_BLUE as alpha selection. 5451080D uses this to resolve its opacity plane, and 4D530808 does the same for a fog lighting pass. Their blue channels are black, so treating LOW_BLUE as an R/B exchange drops data.
Informed by XGCopySurface decompilation, which combines source and inverse dest swizzle, plus notcing how L8 and A8 share the same texture format.
Fixed integer fetches are updated to look widths up through the guest swizzle, so every output channel is scaled by the width it came from.
Previously, host swizzle was being walked, and it was causing some 6 bit channels to be scaled as 5 bits and vice versa.
Co-authored-by: philtimmes <5494151+philtimmes@users.noreply.github.com>
For now, this adds a depth clamp override to both backends that's kept disabled by default.
494707EE needs this for its setup draws that feed its lighting passes. It could be that guest clipping / host near and flare Z planes aren't cleanly interchangeable at the edge of the clip volume.
The fetch constant carries independent signed exponent biases in [-16, 15] for the horizontal and vertical LOD gradients. Scale the H and V gradients by exp2(lod + bias_h) and exp2(lod + bias_v) respectively in the computed LOD sample path. getCompTexLOD keeps returning the raw queried LOD, treating the adjustment like the fetch-constant LOD bias, which is also not folded into it. Zero, the common case, is a no-op.
On the cube implicit-LOD path, where no explicit gradients exist to scale, the greater of the two biases is added to the LOD bias instead - exact when both are equal, erring towards a blurrier mip otherwise.
Co-Authored-By: Herman S. <429230+has207@users.noreply.github.com>
A non-zero value in bits 0:30 of the kill flag kills the vertex, tested on the integer bits rather than as a float comparison so denormal flushing can't affect the result, matching the DXBC translator. With PA_CL_CLIP_CNTL::vtx_kill_or, the position W is set to NaN, killing the whole primitive if any of its vertices requests the kill, with the "and" operator, a dedicated cull distance after the user clip plane cull distances is set to -1, culling the primitive only when all of its vertices request it.
Co-authored-by: Herman S. <429230+has207@users.noreply.github.com>
Explicit gradients of the direction reconstructed from the guest S/T/face coordinates pick the wrong mip on Vulkan, so sample cube fetches that use computed LOD without register gradients with implicit LOD and the accumulated LOD bias (fetch constant + register LOD + instruction bias) as the Bias image operand instead. This also makes tfetchCube consistent with getCompTexLOD, which already queries the implicit LOD. Register-gradient cube fetches keep explicit gradients in cube space, and other dimensions keep explicit gradients, matching the DXBC path. Gradient setup is skipped entirely on the implicit-LOD path.
Co-authored-by: Herman S. <429230+has207@users.noreply.github.com>
The check is moved after the anisotropy resolution (and the anisotropic override) so it sees, and can reset, the final filter state.
Co-authored-by: Herman S. <429230+has207@users.noreply.github.com>
Avoids a TRANSFER_WRITE -> TRANSFER_WRITE hazard identified by the Vulkan validation layer when a texture is reuploaded (after guest memory invalidation) without having been used for drawing in between, leaving it in the transfer destination usage with no layout transition to order the copies.
Co-authored-by: Herman S. <429230+has207@users.noreply.github.com>
545407D4's UI shader uses tfetch1D with a nonreplicated source swizzle and a 2D constant. The way we have it set up is causing the translators to sample with Y set to 0.
Handle this as 2D in the translators. Normal scalar tfetch1D untouched.
Fixes visibility popping in 555308B6 and 5553080B.
Unnormalized texture coordinates address texels of the mip level being sampled, not always the base level. The titles in question lock the fetch constant to one mip and address that mip's grid. The denominator was still the base level size, so each reduction after the first read garbage.
So the locked size is now used for 2D fetches (point filter, clamp-to-edge) with MipMinLevel == MipMaxLevel. Everything else keeps the base level denominator until a shared effective LOD model exists.
Write the bias into round_bias_temp as intended in both packed 8/16/32bpp path and 16_16_16_16.
The copysign rounding bias was targeted the eM register, and destroying the scaled value, then added round_bias_temp, which nothing had written.
8_8_8_8_GAMMA resolve update:
This hopefully settles how safe resolves of gamma RTs are handled, because it's almost certainly not settled by destination.
It's now believed that gamma sources seen by resolve are always being decoded, and that's a decode that's keyed only on the source format, where every destination is fed linear values, and any encode is now just removed rather than conditional.
These sources are re-alised as plain before resolving, which is the same exact idea that preserves float 2_10_10_10 bits through a resolve too.
Integer texture fetch scaling update:
Unsigned-biased components were using the regular unsigned scale even though the conversion had remapped the sample [0, 1] to [-1, 1].
Scale bits data is now increased from 5 to 6 bits and use the new bit to mark unsigned-biased components. DXBC/SPIR-V use half of the unsigned scale and add a -0.5 offset.
This fixes post-processing in 415608B2 and will likely improve other titles w/ EDRAM transports through affected textures, but perhaps not so dramatically.
Roughly matching D3D12, R16G16B16A16_UNORM is used for gamma RTs when sampled and blendable color attachments are supported. The host target is kept linear for blending, and converted during ownership transfers, clears, and readbacks.
Shares the PWL conversion helpers between the translator and RT cache. Raw gamma bytes are decoded to safe linear midpoints so round trips preserve the original bytes.
Implements resolution scale selection, per surface, for both backends.
Surfaces with pitches at or below the defined threshold render at 1x1 while everything else scales. This catches downscaled post-process chains that gain nothing from upscaling and often break from it. 0 leaves everything scaled.
Class is a function of pitch and MSAA samples, baked into RenderTargetKey, and the caches create, clear, transfer, and dump per that class. Transfer shaders carry separate dest and source scales. Pixel shaders get a modification bit so 1x1 draws translate with 1x addressing for param_gen and memexport, and per-draw viewports, point sizes, and divisors pull from RT cache instead of draw_resolution_scale_x/y.
1x1 resolves copy unscaled data straight into shared memory and unmark the range from scaled resolve page tracking, so textures pick up the correct copy. Readback resolves skip downscaling when appropriate. ZPD normalizes per segment so mixed reports don't undercount.
Host RT only. ROV/FSI EDRAM layout assumes a single global scale and, while implementing support for scale selection is probably doable, it's likely an enormous task.
Implements readback for resolution-scaled resolves in both D3D12 & Vulkan backends, mostly mirroring Xenia Edge's approach, with a few small adjustments.
The scaled resolve buffer stores each texel as a group aligned scale_x by scale_y block. A small CS now reverses that packing per 32x32 tile and writes 1x. So far, this is Edge's approach verbatim. This slightly diverges from Edge since now the source window is bound at the written extent, not the texture base & the tail after the last whole tile is no longer copied.
Dest format and texel size are the same ResolveInfo the written extent was calculated with rather than rereading RB_COPY_DEST_INFO, which probably matters for depth resolves where the register holds k_8. 128bpp dests, extents that aren't group-aligned, and extents outside the scaled range skip readback.
This resolves the TODO in GetColorClearShaderConstants about which 32 bit portion is in which register.
For 64bpp formats, RB_COLOR_CLEAR_LO holds the lower 32 bits of the packed clear value, and RB_COLOR_CLEAR holds the upper 32 bits.