mirror of
https://github.com/izzy2lost/xenia-edge.git
synced 2026-07-06 00:20:26 -07:00
[D3D12] Fix cross-format EDRAM data written to gamma_unorm16 render targets
The color_packed_in_r0x_and_r1x output path used UBFE to write uint values to a float-typed R16G16B16A16_UNORM output, type-punning them as denormalized floats (~0). Affeced transfers: k_2_10_10_10_FLOAT → k_8_8_8_8_GAMMA in Halo Reach cause black textures on rocks and plants. To work around this we extracts 4 gamma bytes from the packed EDRAM dword and convert them to linear floats using midpoint encoding — centering values in the valid UNORM16 range so they survive the round-trip through PreSaturatedLinearToPWLGamma's trunc() in the dump shader.
This commit is contained in:
@@ -3482,6 +3482,75 @@ D3D12RenderTargetCache::GetOrCreateTransferPipelines(TransferShaderKey key) {
|
||||
if (dest_color_format == xenos::ColorRenderTargetFormat::k_32_32_FLOAT) {
|
||||
a.OpMov(dxbc::Dest::O(0, 0b0001), dxbc::Src::R(0, dxbc::Src::kXXXX));
|
||||
a.OpMov(dxbc::Dest::O(0, 0b0010), dxbc::Src::R(1, dxbc::Src::kXXXX));
|
||||
} else if (dest_is_gamma_unorm16) {
|
||||
// For gamma_unorm16, only r1.x has a valid packed 32-bit EDRAM
|
||||
// dword (one pixel, since gamma_unorm16 is 32bpp Xenos / 64bpp
|
||||
// host). Reinterpret as k_8_8_8_8_GAMMA (4 gamma-encoded bytes)
|
||||
// and convert to linear float for R16G16B16A16_UNORM output.
|
||||
//
|
||||
// Use midpoint encoding to survive the UNORM16 quantization
|
||||
// round-trip through PreSaturatedLinearToPWLGamma (which uses
|
||||
// trunc()). Storing the exact PWLGammaToLinear result puts values
|
||||
// at the lower boundary of the valid range, where UNORM16
|
||||
// quantization can push them below threshold causing +/-1 byte
|
||||
// errors that corrupt cross-format EDRAM reinterpretation.
|
||||
//
|
||||
// For gamma byte B, the midpoint linear value is:
|
||||
// Piece 0 (B < 64): F = (B + 0.5) / 1023.0
|
||||
// Piece 1 (64<=B<96): F = (B - 31.5) / 511.5
|
||||
// Piece 2 (96<=B<192): F = (B - 63.5) / 255.75
|
||||
// Piece 3 (B >= 192): F = (B - 127.5) / 127.875
|
||||
// Using MAd form: F = B * recip + offset.
|
||||
|
||||
// Extract 4 bytes: r1.xyzw = [R, G, B, A] as uint.
|
||||
a.OpUBFE(dxbc::Dest::R(1), dxbc::Src::LU(8, 8, 8, 8),
|
||||
dxbc::Src::LU(0, 8, 16, 24),
|
||||
dxbc::Src::R(1, dxbc::Src::kXXXX));
|
||||
|
||||
// Alpha: o0.w = float(A) / 255.0 (no gamma conversion).
|
||||
a.OpUToF(dxbc::Dest::R(0, 0b1000), dxbc::Src::R(1, dxbc::Src::kWWWW));
|
||||
a.OpMul(dxbc::Dest::O(0, 0b1000), dxbc::Src::R(0, dxbc::Src::kWWWW),
|
||||
dxbc::Src::LF(1.0f / 255.0f));
|
||||
|
||||
// RGB: per-channel midpoint encoding using r0.xy as (recip,
|
||||
// offset) and r2.x as comparison temp.
|
||||
for (uint32_t j = 0; j < 3; ++j) {
|
||||
// Default to piece 0.
|
||||
a.OpMov(dxbc::Dest::R(0, 0b0001), dxbc::Src::LF(1.0f / 1023.0f));
|
||||
a.OpMov(dxbc::Dest::R(0, 0b0010), dxbc::Src::LF(0.5f / 1023.0f));
|
||||
// Piece 1: byte >= 64.
|
||||
a.OpUGE(dxbc::Dest::R(2, 0b0001), dxbc::Src::R(1).Select(j),
|
||||
dxbc::Src::LU(64));
|
||||
a.OpMovC(dxbc::Dest::R(0, 0b0001), dxbc::Src::R(2, dxbc::Src::kXXXX),
|
||||
dxbc::Src::LF(1.0f / 511.5f),
|
||||
dxbc::Src::R(0, dxbc::Src::kXXXX));
|
||||
a.OpMovC(dxbc::Dest::R(0, 0b0010), dxbc::Src::R(2, dxbc::Src::kXXXX),
|
||||
dxbc::Src::LF(-31.5f / 511.5f),
|
||||
dxbc::Src::R(0, dxbc::Src::kYYYY));
|
||||
// Piece 2: byte >= 96.
|
||||
a.OpUGE(dxbc::Dest::R(2, 0b0001), dxbc::Src::R(1).Select(j),
|
||||
dxbc::Src::LU(96));
|
||||
a.OpMovC(dxbc::Dest::R(0, 0b0001), dxbc::Src::R(2, dxbc::Src::kXXXX),
|
||||
dxbc::Src::LF(1.0f / 255.75f),
|
||||
dxbc::Src::R(0, dxbc::Src::kXXXX));
|
||||
a.OpMovC(dxbc::Dest::R(0, 0b0010), dxbc::Src::R(2, dxbc::Src::kXXXX),
|
||||
dxbc::Src::LF(-63.5f / 255.75f),
|
||||
dxbc::Src::R(0, dxbc::Src::kYYYY));
|
||||
// Piece 3: byte >= 192.
|
||||
a.OpUGE(dxbc::Dest::R(2, 0b0001), dxbc::Src::R(1).Select(j),
|
||||
dxbc::Src::LU(192));
|
||||
a.OpMovC(dxbc::Dest::R(0, 0b0001), dxbc::Src::R(2, dxbc::Src::kXXXX),
|
||||
dxbc::Src::LF(1.0f / 127.875f),
|
||||
dxbc::Src::R(0, dxbc::Src::kXXXX));
|
||||
a.OpMovC(dxbc::Dest::R(0, 0b0010), dxbc::Src::R(2, dxbc::Src::kXXXX),
|
||||
dxbc::Src::LF(-127.5f / 127.875f),
|
||||
dxbc::Src::R(0, dxbc::Src::kYYYY));
|
||||
// F = float(byte) * recip + offset.
|
||||
a.OpUToF(dxbc::Dest::R(2, 0b0001), dxbc::Src::R(1).Select(j));
|
||||
a.OpMAd(dxbc::Dest::O(0, 1 << j), dxbc::Src::R(2, dxbc::Src::kXXXX),
|
||||
dxbc::Src::R(0, dxbc::Src::kXXXX),
|
||||
dxbc::Src::R(0, dxbc::Src::kYYYY));
|
||||
}
|
||||
} else {
|
||||
for (uint32_t i = 0; i < 2; ++i) {
|
||||
a.OpUBFE(dxbc::Dest::O(0, 0b11 << (i * 2)), dxbc::Src::LU(16),
|
||||
|
||||
Reference in New Issue
Block a user