[GPU] Texture integer scaling fetches; 8_8_8_8_GAMMA resolve
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.
This commit is contained in:
committed by
Radosław Gliński
parent
573cce0fe2
commit
d119505289
@@ -3992,6 +3992,13 @@ XE_NOINLINE void D3D12CommandProcessor::UpdateSystemConstantValues_Impl(
|
||||
texture_signs_uint =
|
||||
(texture_signs_uint & ~texture_signs_mask) | texture_signs_shifted;
|
||||
// cache misses here, we're accessing the texture bindings out of order
|
||||
uint32_t texture_integer_scale_bits =
|
||||
texture_cache_->GetActiveIntegerScaleBits(texture_index);
|
||||
update_dirty_uint32_cmp(
|
||||
system_constants_.texture_integer_scale_bits[texture_index],
|
||||
texture_integer_scale_bits);
|
||||
system_constants_.texture_integer_scale_bits[texture_index] =
|
||||
texture_integer_scale_bits;
|
||||
textures_resolution_scaled |=
|
||||
uint32_t(texture_cache_->IsActiveTextureResolutionScaled(texture_index))
|
||||
<< texture_index;
|
||||
|
||||
@@ -172,10 +172,10 @@ class D3D12TextureCache final : public TextureCache {
|
||||
LoadShaderIndex load_shader_signed;
|
||||
|
||||
// Do NOT add integer DXGI formats to this - they are not filterable, can
|
||||
// only be read with Load, not Sample! If any game is seen using num_format
|
||||
// 1 for fixed-point formats (for floating-point, it's normally set to 1
|
||||
// though), add a constant buffer containing multipliers for the
|
||||
// textures and multiplication to the tfetch implementation.
|
||||
// only be read with Load, not Sample! Games that fetch fixed-point formats
|
||||
// are handled after sampling by scaling the normalized host value back to
|
||||
// the guest integer range (see GetIntegerScaleBits). Keep these as
|
||||
// sampled float/normalized views.
|
||||
|
||||
// Whether the DXGI format, if not uncompressing the texture, consists of
|
||||
// blocks, thus copy regions must be aligned to block size (assuming it's
|
||||
|
||||
@@ -28,6 +28,29 @@ DEFINE_bool(
|
||||
"is necessary for certain games to display the scene graphics).",
|
||||
"GPU");
|
||||
|
||||
DEFINE_bool(
|
||||
resolve_check_number_format, false,
|
||||
"Require the destination number format to match before using fast color "
|
||||
"resolves.\n"
|
||||
"Fast resolves copy the exact EDRAM bits. If a title resolves unsigned "
|
||||
"color data to a signed or integer destination, enabling this forces full "
|
||||
"resolves in the shader so the destination gets repacked instead.\n"
|
||||
"This can fix some garbage shading stemming from format mismatches, but "
|
||||
"it's disabled by default because it can worsen performance in some games "
|
||||
"that realistically don't need it.",
|
||||
"GPU");
|
||||
|
||||
DEFINE_bool(
|
||||
gamma_decode_pwl_resolve, true,
|
||||
"During 8_8_8_8_GAMMA MSAA color resolves, average the samples in linear "
|
||||
"space instead of averaging the encoded PWL gamma values directly.\n"
|
||||
"This is separate from gamma_render_target_as_unorm16. It only applies "
|
||||
"when a full shader resolve reads an 8_8_8_8_GAMMA EDRAM color source. "
|
||||
"Compatible 8_8_8_8 destinations are written back as PWL gamma.\n"
|
||||
"Leave enabled for games that otherwise look overexposed after gamma "
|
||||
"MSAA resolves. Disable only if it causes a title-specific regression.",
|
||||
"GPU");
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
namespace draw_util {
|
||||
@@ -1269,6 +1292,8 @@ bool GetResolveInfo(const RegisterFile& regs, const Memory& memory,
|
||||
color_edram_info.format = uint32_t(color_info.color_format);
|
||||
color_edram_info.format_is_64bpp = is_64bpp;
|
||||
color_edram_info.fill_half_pixel_offset = uint32_t(fill_half_pixel_offset);
|
||||
color_edram_info.decode_pwl_gamma =
|
||||
cvars::gamma_decode_pwl_resolve ? 1u : 0u;
|
||||
if ((fixed_rg16_truncated_to_minus_1_to_1 &&
|
||||
color_info.color_format == xenos::ColorRenderTargetFormat::k_16_16) ||
|
||||
(fixed_rgba16_truncated_to_minus_1_to_1 &&
|
||||
@@ -1317,6 +1342,27 @@ bool GetResolveInfo(const RegisterFile& regs, const Memory& memory,
|
||||
return true;
|
||||
}
|
||||
XE_MSVC_OPTIMIZE_REVERT()
|
||||
|
||||
// Raw resolve is only safe when the destination would read the same bits the
|
||||
// active EDRAM view already stores. Canonical fixed colors are unsigned
|
||||
// fractions and float colors are floats; signed/integer destinations need full
|
||||
// resolve so copy_dest_number can actually repack them.
|
||||
static constexpr bool ColorResolveNumberFormatMatches(
|
||||
xenos::ColorFormat color_format, xenos::SurfaceNumberFormat num_format) {
|
||||
switch (color_format) {
|
||||
case xenos::ColorFormat::k_16_FLOAT:
|
||||
case xenos::ColorFormat::k_16_16_FLOAT:
|
||||
case xenos::ColorFormat::k_16_16_16_16_FLOAT:
|
||||
case xenos::ColorFormat::k_32_FLOAT:
|
||||
case xenos::ColorFormat::k_32_32_FLOAT:
|
||||
case xenos::ColorFormat::k_32_32_32_32_FLOAT:
|
||||
return num_format == xenos::SurfaceNumberFormat::kFloat;
|
||||
default:
|
||||
return num_format ==
|
||||
xenos::SurfaceNumberFormat::kUnsignedRepeatingFraction;
|
||||
}
|
||||
}
|
||||
|
||||
ResolveCopyShaderIndex ResolveInfo::GetCopyShader(
|
||||
uint32_t draw_resolution_scale_x, uint32_t draw_resolution_scale_y,
|
||||
ResolveCopyShaderConstants& constants_out, uint32_t& group_count_x_out,
|
||||
@@ -1325,12 +1371,18 @@ ResolveCopyShaderIndex ResolveInfo::GetCopyShader(
|
||||
bool is_depth = IsCopyingDepth();
|
||||
ResolveEdramInfo edram_info = is_depth ? depth_edram_info : color_edram_info;
|
||||
bool source_is_64bpp = !is_depth && color_edram_info.format_is_64bpp != 0;
|
||||
// Fast color resolve is a raw copy. If copy_dest_number asks for a different
|
||||
// fixed interpretation, full resolve has to do the repack.
|
||||
if (is_depth || (!copy_dest_info.copy_dest_exp_bias &&
|
||||
xenos::IsSingleCopySampleSelected(
|
||||
copy_dest_coordinate_info.copy_sample_select) &&
|
||||
xenos::IsColorResolveFormatBitwiseEquivalent(
|
||||
xenos::ColorRenderTargetFormat(color_edram_info.format),
|
||||
xenos::ColorFormat(copy_dest_info.copy_dest_format)))) {
|
||||
xenos::ColorFormat(copy_dest_info.copy_dest_format)) &&
|
||||
(!cvars::resolve_check_number_format ||
|
||||
ColorResolveNumberFormatMatches(
|
||||
xenos::ColorFormat(copy_dest_info.copy_dest_format),
|
||||
copy_dest_info.copy_dest_number)))) {
|
||||
if (edram_info.msaa_samples >= xenos::MsaaSamples::k4X) {
|
||||
shader = source_is_64bpp ? ResolveCopyShaderIndex::kFast64bpp4xMSAA
|
||||
: ResolveCopyShaderIndex::kFast32bpp4xMSAA;
|
||||
|
||||
@@ -514,6 +514,11 @@ union ResolveEdramInfo {
|
||||
// of the resolve region with the contents of the first surely covered
|
||||
// column / row with resolution scaling.
|
||||
uint32_t fill_half_pixel_offset : 1;
|
||||
// Flag from gamma_decode_pwl_resolve in resolve shader. Some games appear
|
||||
// overexposed unless full 8_8_8_8_GAMMA resolves decode PWL gamma to
|
||||
// linear before MSAA averaging / conversion, then write gamma bytes again
|
||||
// for gamma dests. Off keeps the old byte averaging.
|
||||
uint32_t decode_pwl_gamma : 1;
|
||||
};
|
||||
ResolveEdramInfo() : packed(0) { static_assert_size(*this, sizeof(packed)); }
|
||||
};
|
||||
|
||||
@@ -2178,6 +2178,9 @@ constexpr DxbcShaderTranslator::SystemConstantRdef
|
||||
|
||||
{"xe_edram_blend_constant", ShaderRdefTypeIndex::kFloat4,
|
||||
sizeof(float) * 4},
|
||||
|
||||
{"xe_texture_integer_scale_bits", ShaderRdefTypeIndex::kUint4Array8,
|
||||
sizeof(uint32_t) * 32},
|
||||
};
|
||||
|
||||
void DxbcShaderTranslator::WriteResourceDefinition() {
|
||||
|
||||
@@ -114,7 +114,7 @@ class DxbcShaderTranslator : public ShaderTranslator {
|
||||
// If anything in this is structure is changed in a way not compatible with
|
||||
// the previous layout, invalidate the pipeline storages by increasing this
|
||||
// version number (0xYYYYMMDD)!
|
||||
static constexpr uint32_t kVersion = 0x20251216;
|
||||
static constexpr uint32_t kVersion = 0x20260703;
|
||||
|
||||
enum class DepthStencilMode : uint32_t {
|
||||
kNoModifiers,
|
||||
@@ -401,6 +401,13 @@ class DxbcShaderTranslator : public ShaderTranslator {
|
||||
// The constant blend factor for the respective modes.
|
||||
float edram_blend_constant[4];
|
||||
|
||||
// Integer num_format on fixed textures. Each dword packs the scale needed
|
||||
// to turn normalized host samples back into guest integer values.
|
||||
// bits 0:3 = component_bits - 1
|
||||
// bit 4 = signed
|
||||
// Zero means no scale.
|
||||
uint32_t texture_integer_scale_bits[32];
|
||||
|
||||
private:
|
||||
friend class DxbcShaderTranslator;
|
||||
|
||||
@@ -454,6 +461,8 @@ class DxbcShaderTranslator : public ShaderTranslator {
|
||||
|
||||
kEdramBlendConstant,
|
||||
|
||||
kTextureIntegerScaleBits,
|
||||
|
||||
kCount,
|
||||
};
|
||||
static_assert(
|
||||
|
||||
@@ -2058,6 +2058,40 @@ void DxbcShaderTranslator::ProcessTextureFetchInstruction(
|
||||
a_.OpBreak();
|
||||
a_.OpEndSwitch();
|
||||
}
|
||||
// num_format is applied after signedness. A fixed-point format's host
|
||||
// view returns normalized values, so for an integer num_format restore
|
||||
// the guest integer range here.
|
||||
uint32_t integer_scale_bits_temp = PushSystemTemp();
|
||||
uint32_t integer_scale_temp = PushSystemTemp();
|
||||
dxbc::Dest integer_scale_bits_dest(dxbc::Dest::R(
|
||||
integer_scale_bits_temp, used_result_nonzero_components));
|
||||
dxbc::Src integer_scale_bits_src(dxbc::Src::R(integer_scale_bits_temp));
|
||||
dxbc::Dest integer_scale_dest(
|
||||
dxbc::Dest::R(integer_scale_temp, used_result_nonzero_components));
|
||||
dxbc::Src integer_scale_src(dxbc::Src::R(integer_scale_temp));
|
||||
dxbc::Src integer_scale_bits_packed = LoadSystemConstant(
|
||||
SystemConstants::Index::kTextureIntegerScaleBits,
|
||||
offsetof(SystemConstants, texture_integer_scale_bits) +
|
||||
sizeof(uint32_t) * tfetch_index,
|
||||
dxbc::Src::kXXXX);
|
||||
// Uniform early out. Zero means leave the sample alone. Only integer
|
||||
// num_format on fixed textures has scale bits.
|
||||
a_.OpIf(true, integer_scale_bits_packed);
|
||||
a_.OpUBFE(integer_scale_bits_dest, dxbc::Src::LU(5),
|
||||
dxbc::Src::LU(0, 5, 10, 15), integer_scale_bits_packed);
|
||||
a_.OpAnd(integer_scale_dest, integer_scale_bits_src, dxbc::Src::LU(0xF));
|
||||
a_.OpIAdd(integer_scale_dest, integer_scale_src, dxbc::Src::LU(1));
|
||||
a_.OpUShR(integer_scale_bits_dest, integer_scale_bits_src,
|
||||
dxbc::Src::LU(4));
|
||||
a_.OpIAdd(integer_scale_dest, integer_scale_src, -integer_scale_bits_src);
|
||||
a_.OpIShL(integer_scale_dest, dxbc::Src::LU(1), integer_scale_src);
|
||||
a_.OpIAdd(integer_scale_dest, integer_scale_src, dxbc::Src::LI(-1));
|
||||
a_.OpUToF(integer_scale_dest, integer_scale_src);
|
||||
a_.OpMul(
|
||||
dxbc::Dest::R(system_temp_result_, used_result_nonzero_components),
|
||||
dxbc::Src::R(system_temp_result_), integer_scale_src);
|
||||
a_.OpEndIf();
|
||||
PopSystemTemp(2);
|
||||
}
|
||||
if (signs_temp != UINT32_MAX) {
|
||||
PopSystemTemp();
|
||||
|
||||
@@ -93,57 +93,103 @@
|
||||
#define kXenosDepthRenderTargetFormat_D24S8 0u
|
||||
#define kXenosDepthRenderTargetFormat_D24FS8 1u
|
||||
|
||||
#define kXenosSurfaceNumberFormat_UnsignedRepeatingFraction 0u
|
||||
#define kXenosSurfaceNumberFormat_SignedRepeatingFraction 1u
|
||||
#define kXenosSurfaceNumberFormat_UnsignedInteger 2u
|
||||
#define kXenosSurfaceNumberFormat_SignedInteger 3u
|
||||
#define kXenosSurfaceNumberFormat_Float 7u
|
||||
|
||||
// ColorFormat packing, according to the Direct3D 11.3 functional specification.
|
||||
|
||||
uint XePackR5G5B5A1UNorm(float4_xe f) {
|
||||
uint4_xe n = uint4_xe(saturate_xe(f) * float2_xe(31.0f, 1.0f).xxxy + 0.5f);
|
||||
uint XePackFixed(float value, uint bits, uint num_format) {
|
||||
uint unsigned_max = (1u << bits) - 1u;
|
||||
uint packed = 0u;
|
||||
if (num_format == kXenosSurfaceNumberFormat_SignedRepeatingFraction) {
|
||||
// Signed fraction uses the positive endpoint, then keeps the destination
|
||||
// bit width.
|
||||
float positive_max = float((1u << (bits - 1u)) - 1u);
|
||||
packed = uint(int(min(max(value, -1.0f), 1.0f) * positive_max +
|
||||
(value >= 0.0f ? 0.5f : -0.5f)));
|
||||
} else if (num_format == kXenosSurfaceNumberFormat_UnsignedInteger) {
|
||||
packed = uint(min(max(value, 0.0f), float(unsigned_max)) + 0.5f);
|
||||
} else if (num_format == kXenosSurfaceNumberFormat_SignedInteger) {
|
||||
int signed_min = -int(1u << (bits - 1u));
|
||||
int signed_max = int((1u << (bits - 1u)) - 1u);
|
||||
packed = uint(int(min(max(value, float(signed_min)), float(signed_max)) +
|
||||
(value >= 0.0f ? 0.5f : -0.5f)));
|
||||
} else {
|
||||
// Unsigned fraction, float-on-fixed, or anything unexpected: keep the
|
||||
// usual unsigned-fraction pack.
|
||||
packed = uint(saturate_xe(value) * float(unsigned_max) + 0.5f);
|
||||
}
|
||||
return packed & unsigned_max;
|
||||
}
|
||||
|
||||
uint4_xe XePackFixed4(float4_xe f, uint4_xe bits, uint num_format) {
|
||||
return uint4_xe(XePackFixed(f.x, bits.x, num_format),
|
||||
XePackFixed(f.y, bits.y, num_format),
|
||||
XePackFixed(f.z, bits.z, num_format),
|
||||
XePackFixed(f.w, bits.w, num_format));
|
||||
}
|
||||
|
||||
uint3_xe XePackFixed3(float3_xe f, uint3_xe bits, uint num_format) {
|
||||
return uint3_xe(XePackFixed(f.x, bits.x, num_format),
|
||||
XePackFixed(f.y, bits.y, num_format),
|
||||
XePackFixed(f.z, bits.z, num_format));
|
||||
}
|
||||
|
||||
uint2_xe XePackFixed2(float2_xe f, uint2_xe bits, uint num_format) {
|
||||
return uint2_xe(XePackFixed(f.x, bits.x, num_format),
|
||||
XePackFixed(f.y, bits.y, num_format));
|
||||
}
|
||||
|
||||
uint XePackR5G5B5A1(float4_xe f, uint num_format) {
|
||||
uint4_xe n = XePackFixed4(f, uint2_xe(5u, 1u).xxxy, num_format);
|
||||
return n.r | (n.g << 5) | (n.b << 10) | (n.a << 15);
|
||||
}
|
||||
|
||||
uint XePackR5G6B5UNorm(float3_xe f) {
|
||||
uint3_xe n = uint3_xe(saturate_xe(f) * float3_xe(31.0f, 63.0f, 31.0f) + 0.5f);
|
||||
uint XePackR5G6B5(float3_xe f, uint num_format) {
|
||||
uint3_xe n = XePackFixed3(f, uint3_xe(5u, 6u, 5u), num_format);
|
||||
return n.r | (n.g << 5) | (n.b << 11);
|
||||
}
|
||||
|
||||
uint XePackR5G5B6UNorm(float3_xe f) {
|
||||
uint3_xe n = uint3_xe(saturate_xe(f) * float3_xe(31.0f, 31.0f, 63.0f) + 0.5f);
|
||||
uint XePackR5G5B6(float3_xe f, uint num_format) {
|
||||
uint3_xe n = XePackFixed3(f, uint3_xe(5u, 5u, 6u), num_format);
|
||||
return n.r | (n.g << 5) | (n.b << 10);
|
||||
}
|
||||
|
||||
uint XePackR8G8B8A8UNorm(float4_xe f) {
|
||||
uint4_xe n = uint4_xe(saturate_xe(f) * 255.0f + 0.5f);
|
||||
uint XePackR8G8B8A8(float4_xe f, uint num_format) {
|
||||
uint4_xe n = XePackFixed4(f, uint_x4_xe(8u), num_format);
|
||||
return n.r | (n.g << 8) | (n.b << 16) | (n.a << 24);
|
||||
}
|
||||
|
||||
uint XePackR10G10B10A2UNorm(float4_xe f) {
|
||||
uint4_xe n = uint4_xe(saturate_xe(f) * float2_xe(1023.0f, 3.0f).xxxy + 0.5f);
|
||||
uint XePackR10G10B10A2(float4_xe f, uint num_format) {
|
||||
uint4_xe n = XePackFixed4(f, uint2_xe(10u, 2u).xxxy, num_format);
|
||||
return n.r | (n.g << 10) | (n.b << 20) | (n.a << 30);
|
||||
}
|
||||
|
||||
uint XePackR4G4B4A4UNorm(float4_xe f) {
|
||||
uint4_xe n = uint4_xe(saturate_xe(f) * 15.0f + 0.5f);
|
||||
uint XePackR4G4B4A4(float4_xe f, uint num_format) {
|
||||
uint4_xe n = XePackFixed4(f, uint_x4_xe(4u), num_format);
|
||||
return n.r | (n.g << 4) | (n.b << 8) | (n.a << 12);
|
||||
}
|
||||
|
||||
uint XePackR11G11B10UNorm(float3_xe f) {
|
||||
uint3_xe n =
|
||||
uint3_xe(saturate_xe(f) * float3_xe(2047.0f, 2047.0f, 1023.0f) + 0.5);
|
||||
uint XePackR11G11B10(float3_xe f, uint num_format) {
|
||||
uint3_xe n = XePackFixed3(f, uint3_xe(11u, 11u, 10u), num_format);
|
||||
return n.r | (n.g << 11) | (n.b << 22);
|
||||
}
|
||||
|
||||
uint XePackR10G11B11UNorm(float3_xe f) {
|
||||
uint3_xe n =
|
||||
uint3_xe(saturate_xe(f) * float3_xe(1023.0f, 2047.0f, 2047.0f) + 0.5f);
|
||||
uint XePackR10G11B11(float3_xe f, uint num_format) {
|
||||
uint3_xe n = XePackFixed3(f, uint3_xe(10u, 11u, 11u), num_format);
|
||||
return n.r | (n.g << 10) | (n.b << 21);
|
||||
}
|
||||
|
||||
uint XePackR16G16UNorm(float2_xe f) {
|
||||
uint2_xe n = uint2_xe(saturate_xe(f) * 65535.0f + 0.5f);
|
||||
uint XePackR16G16(float2_xe f, uint num_format) {
|
||||
uint2_xe n = XePackFixed2(f, uint_x2_xe(16u), num_format);
|
||||
return n.r | (n.g << 16);
|
||||
}
|
||||
|
||||
uint2_xe XePackR16G16B16A16UNorm(float4_xe f) {
|
||||
uint4_xe n = uint4_xe(saturate_xe(f) * 65535.0f + 0.5f);
|
||||
uint2_xe XePackR16G16B16A16(float4_xe f, uint num_format) {
|
||||
uint4_xe n = XePackFixed4(f, uint_x4_xe(16u), num_format);
|
||||
return n.rb | (n.ga << 16);
|
||||
}
|
||||
|
||||
@@ -162,40 +208,43 @@ uint2_xe XePackR16G16B16A16Edram(float4_xe f) {
|
||||
|
||||
uint2_xe XePack16bpp4PixelsInUInt2(float4_xe pixel_0, float4_xe pixel_1,
|
||||
float4_xe pixel_2, float4_xe pixel_3,
|
||||
uint format) {
|
||||
uint format, uint num_format) {
|
||||
uint2_xe packed;
|
||||
switch (format) {
|
||||
case kXenosFormat_1_5_5_5:
|
||||
packed.x = XePackR5G5B5A1UNorm(pixel_0) |
|
||||
(XePackR5G5B5A1UNorm(pixel_1) << 16u);
|
||||
packed.y = XePackR5G5B5A1UNorm(pixel_2) |
|
||||
(XePackR5G5B5A1UNorm(pixel_3) << 16u);
|
||||
packed.x = XePackR5G5B5A1(pixel_0, num_format) |
|
||||
(XePackR5G5B5A1(pixel_1, num_format) << 16u);
|
||||
packed.y = XePackR5G5B5A1(pixel_2, num_format) |
|
||||
(XePackR5G5B5A1(pixel_3, num_format) << 16u);
|
||||
break;
|
||||
case kXenosFormat_5_6_5:
|
||||
packed.x = XePackR5G6B5UNorm(pixel_0.rgb) |
|
||||
(XePackR5G6B5UNorm(pixel_1.rgb) << 16u);
|
||||
packed.y = XePackR5G6B5UNorm(pixel_2.rgb) |
|
||||
(XePackR5G6B5UNorm(pixel_3.rgb) << 16u);
|
||||
packed.x = XePackR5G6B5(pixel_0.rgb, num_format) |
|
||||
(XePackR5G6B5(pixel_1.rgb, num_format) << 16u);
|
||||
packed.y = XePackR5G6B5(pixel_2.rgb, num_format) |
|
||||
(XePackR5G6B5(pixel_3.rgb, num_format) << 16u);
|
||||
break;
|
||||
case kXenosFormat_6_5_5:
|
||||
packed.x = XePackR5G5B6UNorm(pixel_0.rgb) |
|
||||
(XePackR5G5B6UNorm(pixel_1.rgb) << 16u);
|
||||
packed.y = XePackR5G5B6UNorm(pixel_2.rgb) |
|
||||
(XePackR5G5B6UNorm(pixel_3.rgb) << 16u);
|
||||
packed.x = XePackR5G5B6(pixel_0.rgb, num_format) |
|
||||
(XePackR5G5B6(pixel_1.rgb, num_format) << 16u);
|
||||
packed.y = XePackR5G5B6(pixel_2.rgb, num_format) |
|
||||
(XePackR5G5B6(pixel_3.rgb, num_format) << 16u);
|
||||
break;
|
||||
case kXenosFormat_8_8:
|
||||
packed.x = XePackR8G8B8A8UNorm(float4_xe(pixel_0.rg, pixel_1.rg));
|
||||
packed.y = XePackR8G8B8A8UNorm(float4_xe(pixel_2.rg, pixel_3.rg));
|
||||
packed.x =
|
||||
XePackR8G8B8A8(float4_xe(pixel_0.rg, pixel_1.rg), num_format);
|
||||
packed.y =
|
||||
XePackR8G8B8A8(float4_xe(pixel_2.rg, pixel_3.rg), num_format);
|
||||
break;
|
||||
case kXenosFormat_4_4_4_4:
|
||||
packed.x = XePackR4G4B4A4UNorm(pixel_0) |
|
||||
(XePackR4G4B4A4UNorm(pixel_1) << 16u);
|
||||
packed.y = XePackR4G4B4A4UNorm(pixel_2) |
|
||||
(XePackR4G4B4A4UNorm(pixel_3) << 16u);
|
||||
packed.x = XePackR4G4B4A4(pixel_0, num_format) |
|
||||
(XePackR4G4B4A4(pixel_1, num_format) << 16u);
|
||||
packed.y = XePackR4G4B4A4(pixel_2, num_format) |
|
||||
(XePackR4G4B4A4(pixel_3, num_format) << 16u);
|
||||
break;
|
||||
case kXenosFormat_16:
|
||||
packed = XePackR16G16B16A16UNorm(float4_xe(pixel_0.r, pixel_1.r,
|
||||
pixel_2.r, pixel_3.r));
|
||||
packed = XePackR16G16B16A16(float4_xe(pixel_0.r, pixel_1.r, pixel_2.r,
|
||||
pixel_3.r),
|
||||
num_format);
|
||||
break;
|
||||
default:
|
||||
// Treat as something (16_FLOAT).
|
||||
@@ -208,45 +257,45 @@ uint2_xe XePack16bpp4PixelsInUInt2(float4_xe pixel_0, float4_xe pixel_1,
|
||||
|
||||
uint4_xe XePack32bpp4Pixels(float4_xe pixel_0, float4_xe pixel_1,
|
||||
float4_xe pixel_2, float4_xe pixel_3,
|
||||
uint format) {
|
||||
uint format, uint num_format) {
|
||||
uint4_xe packed;
|
||||
switch (format) {
|
||||
case kXenosFormat_8_8_8_8:
|
||||
// TODO(Triang3l): Investigate 8_8_8_8_A.
|
||||
case kXenosFormat_8_8_8_8_A:
|
||||
case kXenosFormat_8_8_8_8_AS_16_16_16_16:
|
||||
packed.x = XePackR8G8B8A8UNorm(pixel_0);
|
||||
packed.y = XePackR8G8B8A8UNorm(pixel_1);
|
||||
packed.z = XePackR8G8B8A8UNorm(pixel_2);
|
||||
packed.w = XePackR8G8B8A8UNorm(pixel_3);
|
||||
packed.x = XePackR8G8B8A8(pixel_0, num_format);
|
||||
packed.y = XePackR8G8B8A8(pixel_1, num_format);
|
||||
packed.z = XePackR8G8B8A8(pixel_2, num_format);
|
||||
packed.w = XePackR8G8B8A8(pixel_3, num_format);
|
||||
break;
|
||||
case kXenosFormat_2_10_10_10:
|
||||
case kXenosFormat_2_10_10_10_AS_16_16_16_16:
|
||||
packed.x = XePackR10G10B10A2UNorm(pixel_0);
|
||||
packed.y = XePackR10G10B10A2UNorm(pixel_1);
|
||||
packed.z = XePackR10G10B10A2UNorm(pixel_2);
|
||||
packed.w = XePackR10G10B10A2UNorm(pixel_3);
|
||||
packed.x = XePackR10G10B10A2(pixel_0, num_format);
|
||||
packed.y = XePackR10G10B10A2(pixel_1, num_format);
|
||||
packed.z = XePackR10G10B10A2(pixel_2, num_format);
|
||||
packed.w = XePackR10G10B10A2(pixel_3, num_format);
|
||||
break;
|
||||
case kXenosFormat_10_11_11:
|
||||
case kXenosFormat_10_11_11_AS_16_16_16_16:
|
||||
packed.x = XePackR11G11B10UNorm(pixel_0.rgb);
|
||||
packed.y = XePackR11G11B10UNorm(pixel_1.rgb);
|
||||
packed.z = XePackR11G11B10UNorm(pixel_2.rgb);
|
||||
packed.w = XePackR11G11B10UNorm(pixel_3.rgb);
|
||||
packed.x = XePackR11G11B10(pixel_0.rgb, num_format);
|
||||
packed.y = XePackR11G11B10(pixel_1.rgb, num_format);
|
||||
packed.z = XePackR11G11B10(pixel_2.rgb, num_format);
|
||||
packed.w = XePackR11G11B10(pixel_3.rgb, num_format);
|
||||
break;
|
||||
case kXenosFormat_11_11_10:
|
||||
case kXenosFormat_11_11_10_AS_16_16_16_16:
|
||||
packed.x = XePackR10G11B11UNorm(pixel_0.rgb);
|
||||
packed.y = XePackR10G11B11UNorm(pixel_1.rgb);
|
||||
packed.z = XePackR10G11B11UNorm(pixel_2.rgb);
|
||||
packed.w = XePackR10G11B11UNorm(pixel_3.rgb);
|
||||
packed.x = XePackR10G11B11(pixel_0.rgb, num_format);
|
||||
packed.y = XePackR10G11B11(pixel_1.rgb, num_format);
|
||||
packed.z = XePackR10G11B11(pixel_2.rgb, num_format);
|
||||
packed.w = XePackR10G11B11(pixel_3.rgb, num_format);
|
||||
break;
|
||||
case kXenosFormat_16_16_EDRAM:
|
||||
case kXenosFormat_16_16:
|
||||
packed.x = XePackR16G16UNorm(pixel_0.rg);
|
||||
packed.y = XePackR16G16UNorm(pixel_1.rg);
|
||||
packed.z = XePackR16G16UNorm(pixel_2.rg);
|
||||
packed.w = XePackR16G16UNorm(pixel_3.rg);
|
||||
packed.x = XePackR16G16(pixel_0.rg, num_format);
|
||||
packed.y = XePackR16G16(pixel_1.rg, num_format);
|
||||
packed.z = XePackR16G16(pixel_2.rg, num_format);
|
||||
packed.w = XePackR16G16(pixel_3.rg, num_format);
|
||||
break;
|
||||
case kXenosFormat_16_16_FLOAT:
|
||||
packed.x = pack_half_2x16_xe(float2_xe(pixel_0.r, pixel_0.g));
|
||||
@@ -267,15 +316,16 @@ uint4_xe XePack32bpp4Pixels(float4_xe pixel_0, float4_xe pixel_1,
|
||||
|
||||
void XePack64bpp4Pixels(float4_xe pixel_0, float4_xe pixel_1,
|
||||
float4_xe pixel_2, float4_xe pixel_3, uint format,
|
||||
uint num_format,
|
||||
out_param_xe(uint4_xe, packed_01),
|
||||
out_param_xe(uint4_xe, packed_23)) {
|
||||
switch (format) {
|
||||
case kXenosFormat_16_16_16_16_EDRAM:
|
||||
case kXenosFormat_16_16_16_16:
|
||||
packed_01.xy = XePackR16G16B16A16UNorm(pixel_0);
|
||||
packed_01.zw = XePackR16G16B16A16UNorm(pixel_1);
|
||||
packed_23.xy = XePackR16G16B16A16UNorm(pixel_2);
|
||||
packed_23.zw = XePackR16G16B16A16UNorm(pixel_3);
|
||||
packed_01.xy = XePackR16G16B16A16(pixel_0, num_format);
|
||||
packed_01.zw = XePackR16G16B16A16(pixel_1, num_format);
|
||||
packed_23.xy = XePackR16G16B16A16(pixel_2, num_format);
|
||||
packed_23.zw = XePackR16G16B16A16(pixel_3, num_format);
|
||||
break;
|
||||
case kXenosFormat_16_16_16_16_FLOAT:
|
||||
packed_01.x = pack_half_2x16_xe(float2_xe(pixel_0.r, pixel_0.g));
|
||||
|
||||
@@ -70,6 +70,7 @@ struct XeResolveInfo {
|
||||
uint edram_base_tiles;
|
||||
uint edram_format;
|
||||
uint edram_format_ints_log2;
|
||||
bool decode_pwl_gamma;
|
||||
uint2_xe resolution_scale;
|
||||
uint2_xe half_pixel_offset_fill_source;
|
||||
uint2_xe edram_offset_scaled;
|
||||
@@ -81,6 +82,7 @@ struct XeResolveInfo {
|
||||
bool dest_is_array;
|
||||
uint dest_slice;
|
||||
uint dest_format;
|
||||
uint dest_num_format;
|
||||
float dest_exp_bias_factor;
|
||||
bool dest_swap;
|
||||
uint dest_row_pitch_macro_tiles;
|
||||
@@ -101,6 +103,7 @@ XeResolveInfo XeResolveGetInfo(param_push_consts_xe) {
|
||||
resolve_info.edram_base_tiles = (edram_info >> 13u) & ((1u << 11u) - 1u);
|
||||
resolve_info.edram_format = (edram_info >> 24u) & ((1u << 4u) - 1u);
|
||||
resolve_info.edram_format_ints_log2 = (edram_info >> 28u) & 1u;
|
||||
resolve_info.decode_pwl_gamma = (edram_info & (1u << 30u)) != 0u;
|
||||
#ifdef XE_RESOLVE_RESOLUTION_SCALED
|
||||
resolve_info.resolution_scale =
|
||||
(uint_x2_xe(coordinate_info) >> uint2_xe(16u, 19u)) & 7u;
|
||||
@@ -132,6 +135,7 @@ XeResolveInfo XeResolveGetInfo(param_push_consts_xe) {
|
||||
resolve_info.dest_is_array = (dest_info & (1u << 3u)) != 0u;
|
||||
resolve_info.dest_slice = (dest_info >> 4u) & ((1u << 3u) - 1u);
|
||||
resolve_info.dest_format = (dest_info >> 7u) & ((1u << 6u) - 1u);
|
||||
resolve_info.dest_num_format = (dest_info >> 13u) & ((1u << 3u) - 1u);
|
||||
resolve_info.dest_exp_bias_factor = int_bits_to_float_xe(
|
||||
(int(dest_info) << (32 - (16 + 6)) >> (32 - 6) << 23) +
|
||||
float_bits_to_int_xe(1.0f));
|
||||
@@ -580,6 +584,127 @@ uint XeResolveEdramPixelStrideBytes(XeResolveInfo resolve_info) {
|
||||
}
|
||||
}
|
||||
|
||||
// Gamma RTs store linear 10 bit color through an 8 bit PWL curve.
|
||||
// Host sRGB is not a substitute. Some titles appear overexposed if full
|
||||
// 8_8_8_8_GAMMA resolves average the encoded bytes directly (5345085D,
|
||||
// 45410934), so we decode before MSAA averaging and encode again only
|
||||
// for compatible 8_8_8_8 destinations.
|
||||
float XePWLGammaToLinear(float gamma) {
|
||||
gamma = saturate_xe(gamma);
|
||||
float scale;
|
||||
float offset;
|
||||
if (gamma >= 96.0f / 255.0f) {
|
||||
if (gamma >= 192.0f / 255.0f) {
|
||||
scale = 8.0f / 1024.0f;
|
||||
offset = -1024.0f;
|
||||
} else {
|
||||
scale = 4.0f / 1024.0f;
|
||||
offset = -256.0f;
|
||||
}
|
||||
} else {
|
||||
if (gamma >= 64.0f / 255.0f) {
|
||||
scale = 2.0f / 1024.0f;
|
||||
offset = -64.0f;
|
||||
} else {
|
||||
scale = 1.0f / 1024.0f;
|
||||
offset = 0.0f;
|
||||
}
|
||||
}
|
||||
float linear_value = gamma * (255.0f * 1024.0f) * scale + offset;
|
||||
linear_value += trunc(linear_value * scale);
|
||||
return linear_value * (1.0f / 1023.0f);
|
||||
}
|
||||
|
||||
float3_xe XePWLGammaToLinear3(float3_xe gamma) {
|
||||
return float3_xe(XePWLGammaToLinear(gamma.x),
|
||||
XePWLGammaToLinear(gamma.y),
|
||||
XePWLGammaToLinear(gamma.z));
|
||||
}
|
||||
|
||||
float4_xe XePWLGammaToLinear4(float4_xe gamma) {
|
||||
return float4_xe(XePWLGammaToLinear(gamma.x),
|
||||
XePWLGammaToLinear(gamma.y),
|
||||
XePWLGammaToLinear(gamma.z),
|
||||
XePWLGammaToLinear(gamma.w));
|
||||
}
|
||||
|
||||
float XeLinearToPWLGamma(float linear_value) {
|
||||
linear_value = saturate_xe(linear_value);
|
||||
float scale;
|
||||
float offset;
|
||||
if (linear_value >= 128.0f / 1023.0f) {
|
||||
if (linear_value >= 512.0f / 1023.0f) {
|
||||
scale = 1023.0f / 8.0f;
|
||||
offset = 128.0f / 255.0f;
|
||||
} else {
|
||||
scale = 1023.0f / 4.0f;
|
||||
offset = 64.0f / 255.0f;
|
||||
}
|
||||
} else {
|
||||
if (linear_value >= 64.0f / 1023.0f) {
|
||||
scale = 1023.0f / 2.0f;
|
||||
offset = 32.0f / 255.0f;
|
||||
} else {
|
||||
scale = 1023.0f;
|
||||
offset = 0.0f;
|
||||
}
|
||||
}
|
||||
return trunc(linear_value * scale) * (1.0f / 255.0f) + offset;
|
||||
}
|
||||
|
||||
float3_xe XeLinearToPWLGamma3(float3_xe linear_value) {
|
||||
return float3_xe(XeLinearToPWLGamma(linear_value.x),
|
||||
XeLinearToPWLGamma(linear_value.y),
|
||||
XeLinearToPWLGamma(linear_value.z));
|
||||
}
|
||||
|
||||
bool XeResolveSourceUsesPWLGamma(XeResolveInfo resolve_info) {
|
||||
return resolve_info.decode_pwl_gamma &&
|
||||
resolve_info.edram_format ==
|
||||
kXenosColorRenderTargetFormat_8_8_8_8_GAMMA;
|
||||
}
|
||||
|
||||
bool XeResolveDestStoresPWLGamma(XeResolveInfo resolve_info) {
|
||||
// Resolve constants don't carry a destination gamma bit. Treat an
|
||||
// unsigned 8_8_8_8 destination as PWL gamma storage.
|
||||
bool dest_number_is_unorm =
|
||||
resolve_info.dest_num_format ==
|
||||
kXenosSurfaceNumberFormat_UnsignedRepeatingFraction;
|
||||
bool dest_is_8888 =
|
||||
resolve_info.dest_format == kXenosFormat_8_8_8_8 ||
|
||||
resolve_info.dest_format == kXenosFormat_8_8_8_8_A ||
|
||||
resolve_info.dest_format ==
|
||||
kXenosFormat_8_8_8_8_AS_16_16_16_16;
|
||||
return dest_number_is_unorm && dest_is_8888;
|
||||
}
|
||||
|
||||
void XeResolvePWLGammaToLinearRGB(inout_param_xe(float4_xe, pixel)) {
|
||||
pixel.rgb = XePWLGammaToLinear3(pixel.rgb);
|
||||
}
|
||||
|
||||
void XeResolveLinearToPWLGammaRGB(inout_param_xe(float4_xe, pixel)) {
|
||||
pixel.rgb = XeLinearToPWLGamma3(pixel.rgb);
|
||||
}
|
||||
|
||||
void XeResolveDecodePWLGammaSource(
|
||||
XeResolveInfo resolve_info, inout_param_xe(float4_xe, pixel)) {
|
||||
// Source gamma is RGB only. 8_8_8_8_GAMMA still stores alpha as ordinary
|
||||
// fixed data, so alpha needs to stay with normal resolve.
|
||||
dont_flatten_xe if (XeResolveSourceUsesPWLGamma(resolve_info)) {
|
||||
XeResolvePWLGammaToLinearRGB(pixel);
|
||||
}
|
||||
}
|
||||
|
||||
void XeResolveEncodePWLGammaDest(
|
||||
XeResolveInfo resolve_info, inout_param_xe(float4_xe, pixel)) {
|
||||
// Only re-encode when the source was PWL gamma and the destination is the
|
||||
// 8_8_8_8 UNORM storage we treat as the same PWL byte stream.
|
||||
dont_flatten_xe if (XeResolveSourceUsesPWLGamma(resolve_info) &&
|
||||
XeResolveDestStoresPWLGamma(resolve_info)) {
|
||||
XeResolveLinearToPWLGammaRGB(pixel);
|
||||
}
|
||||
}
|
||||
|
||||
void XeResolveLoad2RGBAColors(
|
||||
param_byte_buffer_xe(xe_resolve_edram) param_next_after_byte_buffer_xe
|
||||
XeResolveInfo resolve_info, uint address_bytes,
|
||||
@@ -590,11 +715,12 @@ uint XeResolveEdramPixelStrideBytes(XeResolveInfo resolve_info) {
|
||||
address_bytes, pixel_stride_bytes,
|
||||
resolve_info.edram_format_ints_log2, resolve_info.edram_format,
|
||||
pixel_0, pixel_1);
|
||||
XeResolveDecodePWLGammaSource(resolve_info, pixel_0);
|
||||
XeResolveDecodePWLGammaSource(resolve_info, pixel_1);
|
||||
float exp_bias = resolve_info.dest_exp_bias_factor;
|
||||
dont_flatten_xe
|
||||
if (resolve_info.sample_select >= kXenosCopySampleSelect_01) {
|
||||
uint tile_row_stride_bytes = 4u * 80u * resolve_info.resolution_scale.x;
|
||||
// TODO(Triang3l): Gamma-correct resolve for 8_8_8_8_GAMMA.
|
||||
exp_bias *= 0.5f;
|
||||
float4_xe msaa_resolve_pixel_0, msaa_resolve_pixel_1;
|
||||
XeResolveLoad2RGBAUnswappedPixelSamplesFromRaw(
|
||||
@@ -602,6 +728,8 @@ uint XeResolveEdramPixelStrideBytes(XeResolveInfo resolve_info) {
|
||||
address_bytes + tile_row_stride_bytes, pixel_stride_bytes,
|
||||
resolve_info.edram_format_ints_log2, resolve_info.edram_format,
|
||||
msaa_resolve_pixel_0, msaa_resolve_pixel_1);
|
||||
XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_0);
|
||||
XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_1);
|
||||
pixel_0 += msaa_resolve_pixel_0;
|
||||
pixel_1 += msaa_resolve_pixel_1;
|
||||
dont_flatten_xe
|
||||
@@ -614,6 +742,8 @@ uint XeResolveEdramPixelStrideBytes(XeResolveInfo resolve_info) {
|
||||
address_bytes + sample_stride_bytes, pixel_stride_bytes,
|
||||
resolve_info.edram_format_ints_log2, resolve_info.edram_format,
|
||||
msaa_resolve_pixel_0, msaa_resolve_pixel_1);
|
||||
XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_0);
|
||||
XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_1);
|
||||
pixel_0 += msaa_resolve_pixel_0;
|
||||
pixel_1 += msaa_resolve_pixel_1;
|
||||
XeResolveLoad2RGBAUnswappedPixelSamplesFromRaw(
|
||||
@@ -623,12 +753,16 @@ uint XeResolveEdramPixelStrideBytes(XeResolveInfo resolve_info) {
|
||||
pixel_stride_bytes, resolve_info.edram_format_ints_log2,
|
||||
resolve_info.edram_format, msaa_resolve_pixel_0,
|
||||
msaa_resolve_pixel_1);
|
||||
XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_0);
|
||||
XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_1);
|
||||
pixel_0 += msaa_resolve_pixel_0;
|
||||
pixel_1 += msaa_resolve_pixel_1;
|
||||
}
|
||||
}
|
||||
pixel_0 *= exp_bias;
|
||||
pixel_1 *= exp_bias;
|
||||
XeResolveEncodePWLGammaDest(resolve_info, pixel_0);
|
||||
XeResolveEncodePWLGammaDest(resolve_info, pixel_1);
|
||||
dont_flatten_xe if (resolve_info.dest_swap) {
|
||||
pixel_0 = pixel_0.bgra;
|
||||
pixel_1 = pixel_1.bgra;
|
||||
@@ -646,11 +780,14 @@ uint XeResolveEdramPixelStrideBytes(XeResolveInfo resolve_info) {
|
||||
address_bytes, pixel_stride_bytes,
|
||||
resolve_info.edram_format_ints_log2, resolve_info.edram_format,
|
||||
pixel_0, pixel_1, pixel_2, pixel_3);
|
||||
XeResolveDecodePWLGammaSource(resolve_info, pixel_0);
|
||||
XeResolveDecodePWLGammaSource(resolve_info, pixel_1);
|
||||
XeResolveDecodePWLGammaSource(resolve_info, pixel_2);
|
||||
XeResolveDecodePWLGammaSource(resolve_info, pixel_3);
|
||||
float exp_bias = resolve_info.dest_exp_bias_factor;
|
||||
dont_flatten_xe
|
||||
if (resolve_info.sample_select >= kXenosCopySampleSelect_01) {
|
||||
uint tile_row_stride_bytes = 4u * 80u * resolve_info.resolution_scale.x;
|
||||
// TODO(Triang3l): Gamma-correct resolve for 8_8_8_8_GAMMA.
|
||||
exp_bias *= 0.5f;
|
||||
float4_xe msaa_resolve_pixel_0;
|
||||
float4_xe msaa_resolve_pixel_1;
|
||||
@@ -662,6 +799,10 @@ uint XeResolveEdramPixelStrideBytes(XeResolveInfo resolve_info) {
|
||||
resolve_info.edram_format_ints_log2, resolve_info.edram_format,
|
||||
msaa_resolve_pixel_0, msaa_resolve_pixel_1, msaa_resolve_pixel_2,
|
||||
msaa_resolve_pixel_3);
|
||||
XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_0);
|
||||
XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_1);
|
||||
XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_2);
|
||||
XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_3);
|
||||
pixel_0 += msaa_resolve_pixel_0;
|
||||
pixel_1 += msaa_resolve_pixel_1;
|
||||
pixel_2 += msaa_resolve_pixel_2;
|
||||
@@ -677,6 +818,10 @@ uint XeResolveEdramPixelStrideBytes(XeResolveInfo resolve_info) {
|
||||
resolve_info.edram_format_ints_log2, resolve_info.edram_format,
|
||||
msaa_resolve_pixel_0, msaa_resolve_pixel_1, msaa_resolve_pixel_2,
|
||||
msaa_resolve_pixel_3);
|
||||
XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_0);
|
||||
XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_1);
|
||||
XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_2);
|
||||
XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_3);
|
||||
pixel_0 += msaa_resolve_pixel_0;
|
||||
pixel_1 += msaa_resolve_pixel_1;
|
||||
pixel_2 += msaa_resolve_pixel_2;
|
||||
@@ -688,6 +833,10 @@ uint XeResolveEdramPixelStrideBytes(XeResolveInfo resolve_info) {
|
||||
pixel_stride_bytes, resolve_info.edram_format_ints_log2,
|
||||
resolve_info.edram_format, msaa_resolve_pixel_0,
|
||||
msaa_resolve_pixel_1, msaa_resolve_pixel_2, msaa_resolve_pixel_3);
|
||||
XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_0);
|
||||
XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_1);
|
||||
XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_2);
|
||||
XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_3);
|
||||
pixel_0 += msaa_resolve_pixel_0;
|
||||
pixel_1 += msaa_resolve_pixel_1;
|
||||
pixel_2 += msaa_resolve_pixel_2;
|
||||
@@ -698,6 +847,10 @@ uint XeResolveEdramPixelStrideBytes(XeResolveInfo resolve_info) {
|
||||
pixel_1 *= exp_bias;
|
||||
pixel_2 *= exp_bias;
|
||||
pixel_3 *= exp_bias;
|
||||
XeResolveEncodePWLGammaDest(resolve_info, pixel_0);
|
||||
XeResolveEncodePWLGammaDest(resolve_info, pixel_1);
|
||||
XeResolveEncodePWLGammaDest(resolve_info, pixel_2);
|
||||
XeResolveEncodePWLGammaDest(resolve_info, pixel_3);
|
||||
dont_flatten_xe if (resolve_info.dest_swap) {
|
||||
pixel_0 = pixel_0.bgra;
|
||||
pixel_1 = pixel_1.bgra;
|
||||
@@ -721,11 +874,14 @@ uint XeResolveEdramPixelStrideBytes(XeResolveInfo resolve_info) {
|
||||
address_bytes, pixel_stride_bytes,
|
||||
resolve_info.edram_format_ints_log2, resolve_info.edram_format,
|
||||
resolve_info.dest_swap, pixels_0123, pixels_4567);
|
||||
dont_flatten_xe if (XeResolveSourceUsesPWLGamma(resolve_info)) {
|
||||
pixels_0123 = XePWLGammaToLinear4(pixels_0123);
|
||||
pixels_4567 = XePWLGammaToLinear4(pixels_4567);
|
||||
}
|
||||
float exp_bias = resolve_info.dest_exp_bias_factor;
|
||||
dont_flatten_xe
|
||||
if (resolve_info.sample_select >= kXenosCopySampleSelect_01) {
|
||||
uint tile_row_stride_bytes = 4u * 80u * resolve_info.resolution_scale.x;
|
||||
// TODO(Triang3l): Gamma-correct resolve for 8_8_8_8_GAMMA.
|
||||
exp_bias *= 0.5f;
|
||||
float4_xe msaa_resolve_pixels_0123, msaa_resolve_pixels_4567;
|
||||
XeResolveLoad8RedPixelSamplesFromRaw(
|
||||
@@ -734,6 +890,12 @@ uint XeResolveEdramPixelStrideBytes(XeResolveInfo resolve_info) {
|
||||
resolve_info.edram_format_ints_log2, resolve_info.edram_format,
|
||||
resolve_info.dest_swap, msaa_resolve_pixels_0123,
|
||||
msaa_resolve_pixels_4567);
|
||||
dont_flatten_xe if (XeResolveSourceUsesPWLGamma(resolve_info)) {
|
||||
msaa_resolve_pixels_0123 =
|
||||
XePWLGammaToLinear4(msaa_resolve_pixels_0123);
|
||||
msaa_resolve_pixels_4567 =
|
||||
XePWLGammaToLinear4(msaa_resolve_pixels_4567);
|
||||
}
|
||||
pixels_0123 += msaa_resolve_pixels_0123;
|
||||
pixels_4567 += msaa_resolve_pixels_4567;
|
||||
dont_flatten_xe
|
||||
@@ -747,6 +909,12 @@ uint XeResolveEdramPixelStrideBytes(XeResolveInfo resolve_info) {
|
||||
resolve_info.edram_format_ints_log2, resolve_info.edram_format,
|
||||
resolve_info.dest_swap, msaa_resolve_pixels_0123,
|
||||
msaa_resolve_pixels_4567);
|
||||
dont_flatten_xe if (XeResolveSourceUsesPWLGamma(resolve_info)) {
|
||||
msaa_resolve_pixels_0123 =
|
||||
XePWLGammaToLinear4(msaa_resolve_pixels_0123);
|
||||
msaa_resolve_pixels_4567 =
|
||||
XePWLGammaToLinear4(msaa_resolve_pixels_4567);
|
||||
}
|
||||
pixels_0123 += msaa_resolve_pixels_0123;
|
||||
pixels_4567 += msaa_resolve_pixels_4567;
|
||||
XeResolveLoad8RedPixelSamplesFromRaw(
|
||||
@@ -756,6 +924,12 @@ uint XeResolveEdramPixelStrideBytes(XeResolveInfo resolve_info) {
|
||||
pixel_stride_bytes, resolve_info.edram_format_ints_log2,
|
||||
resolve_info.edram_format, resolve_info.dest_swap,
|
||||
msaa_resolve_pixels_0123, msaa_resolve_pixels_4567);
|
||||
dont_flatten_xe if (XeResolveSourceUsesPWLGamma(resolve_info)) {
|
||||
msaa_resolve_pixels_0123 =
|
||||
XePWLGammaToLinear4(msaa_resolve_pixels_0123);
|
||||
msaa_resolve_pixels_4567 =
|
||||
XePWLGammaToLinear4(msaa_resolve_pixels_4567);
|
||||
}
|
||||
pixels_0123 += msaa_resolve_pixels_0123;
|
||||
pixels_4567 += msaa_resolve_pixels_4567;
|
||||
}
|
||||
|
||||
@@ -42,7 +42,8 @@ entry_inputs_end_code_begin_compute_xe
|
||||
resolve_info, pixel_index),
|
||||
pixel_0, pixel_1, pixel_2, pixel_3);
|
||||
uint2_xe packed = XePack16bpp4PixelsInUInt2(
|
||||
pixel_0, pixel_1, pixel_2, pixel_3, resolve_info.dest_format);
|
||||
pixel_0, pixel_1, pixel_2, pixel_3, resolve_info.dest_format,
|
||||
resolve_info.dest_num_format);
|
||||
dont_flatten_xe
|
||||
if (pixel_index.x == 0u &&
|
||||
resolve_info.half_pixel_offset_fill_source.x != 0u) {
|
||||
|
||||
@@ -44,7 +44,8 @@ entry_inputs_end_code_begin_compute_xe
|
||||
resolve_info, pixel_index),
|
||||
pixel_0, pixel_1, pixel_2, pixel_3);
|
||||
uint4_xe packed = XePack32bpp4Pixels(pixel_0, pixel_1, pixel_2, pixel_3,
|
||||
resolve_info.dest_format);
|
||||
resolve_info.dest_format,
|
||||
resolve_info.dest_num_format);
|
||||
dont_flatten_xe
|
||||
if (pixel_index.x == 0u &&
|
||||
resolve_info.half_pixel_offset_fill_source.x != 0u) {
|
||||
|
||||
@@ -43,7 +43,8 @@ entry_inputs_end_code_begin_compute_xe
|
||||
pixel_0, pixel_1, pixel_2, pixel_3);
|
||||
uint4_xe packed_01, packed_23;
|
||||
XePack64bpp4Pixels(pixel_0, pixel_1, pixel_2, pixel_3,
|
||||
resolve_info.dest_format, packed_01, packed_23);
|
||||
resolve_info.dest_format,
|
||||
resolve_info.dest_num_format, packed_01, packed_23);
|
||||
dont_flatten_xe
|
||||
if (pixel_index.x == 0u &&
|
||||
resolve_info.half_pixel_offset_fill_source.x != 0u) {
|
||||
|
||||
@@ -56,7 +56,7 @@ entry_inputs_end_code_begin_compute_xe
|
||||
// TODO(Triang3l): Investigate formats 8_A and 8_B.
|
||||
byte_buffer_align8_store8_xe(
|
||||
xe_resolve_dest, XeResolveDestPixelAddress(resolve_info, pixel_index, 0u),
|
||||
uint2_xe(XePackR8G8B8A8UNorm(pixels_0123),
|
||||
XePackR8G8B8A8UNorm(pixels_4567)));
|
||||
uint2_xe(XePackR8G8B8A8(pixels_0123, resolve_info.dest_num_format),
|
||||
XePackR8G8B8A8(pixels_4567, resolve_info.dest_num_format)));
|
||||
}
|
||||
entry_code_end_compute_xe
|
||||
|
||||
@@ -49,6 +49,8 @@ cbuffer xe_system_cbuffer : register(b0) {
|
||||
uint4 xe_edram_rt_blend_factors_ops;
|
||||
|
||||
float4 xe_edram_blend_constant;
|
||||
|
||||
uint4 xe_texture_integer_scale_bits[8];
|
||||
};
|
||||
|
||||
struct XeHSControlPointInputIndexed {
|
||||
|
||||
@@ -252,6 +252,10 @@ void SpirvShaderTranslator::StartTranslation() {
|
||||
type_uint4_, builder_->makeUintConstant(4), sizeof(uint32_t) * 4);
|
||||
builder_->addDecoration(type_uint4_array_4, spv::DecorationArrayStride,
|
||||
sizeof(uint32_t) * 4);
|
||||
spv::Id type_uint4_array_8 = builder_->makeArrayType(
|
||||
type_uint4_, builder_->makeUintConstant(8), sizeof(uint32_t) * 4);
|
||||
builder_->addDecoration(type_uint4_array_8, spv::DecorationArrayStride,
|
||||
sizeof(uint32_t) * 4);
|
||||
spv::Id type_float4_array_6 = builder_->makeArrayType(
|
||||
type_float4_, builder_->makeUintConstant(6), sizeof(float) * 4);
|
||||
builder_->addDecoration(type_float4_array_6, spv::DecorationArrayStride,
|
||||
@@ -317,6 +321,9 @@ void SpirvShaderTranslator::StartTranslation() {
|
||||
type_float4_array_4},
|
||||
{"edram_blend_constant", offsetof(SystemConstants, edram_blend_constant),
|
||||
type_float4_},
|
||||
{"texture_integer_scale_bits",
|
||||
offsetof(SystemConstants, texture_integer_scale_bits),
|
||||
type_uint4_array_8},
|
||||
};
|
||||
id_vector_temp_.clear();
|
||||
id_vector_temp_.reserve(xe::countof(system_constants));
|
||||
|
||||
@@ -34,7 +34,7 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
||||
// TODO(Triang3l): Change to 0xYYYYMMDD once it's out of the rapid
|
||||
// prototyping stage (easier to do small granular updates with an
|
||||
// incremental counter).
|
||||
static constexpr uint32_t kVersion = 8;
|
||||
static constexpr uint32_t kVersion = 9;
|
||||
|
||||
enum class DepthStencilMode : uint32_t {
|
||||
kNoModifiers,
|
||||
@@ -275,6 +275,13 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
||||
|
||||
// The constant blend factor for the respective modes.
|
||||
float edram_blend_constant[4];
|
||||
|
||||
// Integer num_format on fixed textures. Each dword packs the scale needed
|
||||
// to turn normalized host samples back into guest integer values.
|
||||
// bits 0:3 = component_bits - 1
|
||||
// bit 4 = signed.
|
||||
// Zero means no scale.
|
||||
uint32_t texture_integer_scale_bits[32];
|
||||
};
|
||||
|
||||
// Separate constant buffer for user clip planes
|
||||
@@ -905,6 +912,7 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
||||
kSystemConstantEdramRTKeepMask,
|
||||
kSystemConstantEdramRTClamp,
|
||||
kSystemConstantEdramBlendConstant,
|
||||
kSystemConstantTextureIntegerScaleBits,
|
||||
};
|
||||
spv::Id uniform_system_constants_;
|
||||
spv::Id uniform_clip_plane_constants_;
|
||||
|
||||
@@ -2308,6 +2308,82 @@ void SpirvShaderTranslator::ProcessTextureFetchInstruction(
|
||||
}
|
||||
}
|
||||
|
||||
// num_format is applied after signs/gamma. Fixed textures sample as
|
||||
// normalized host values, so integer num_format scales them back to
|
||||
// guest integer units here.
|
||||
id_vector_temp_.clear();
|
||||
id_vector_temp_.push_back(
|
||||
builder_->makeIntConstant(kSystemConstantTextureIntegerScaleBits));
|
||||
id_vector_temp_.push_back(
|
||||
builder_->makeIntConstant(int32_t(fetch_constant_index >> 2)));
|
||||
id_vector_temp_.push_back(
|
||||
builder_->makeIntConstant(int32_t(fetch_constant_index & 3)));
|
||||
spv::Id integer_scale_bits_packed = builder_->createLoad(
|
||||
builder_->createAccessChain(spv::StorageClassUniform,
|
||||
uniform_system_constants_,
|
||||
id_vector_temp_),
|
||||
spv::NoPrecision);
|
||||
{
|
||||
// Uniform early out. Zero means leave the sample alone. Only integer
|
||||
// num_format on fixed textures has scale bits.
|
||||
spv::Id integer_scale_active = builder_->createBinOp(
|
||||
spv::OpINotEqual, type_bool_, integer_scale_bits_packed,
|
||||
builder_->makeUintConstant(0));
|
||||
SpirvBuilder::IfBuilder if_integer_scale(
|
||||
integer_scale_active, spv::SelectionControlMaskNone, *builder_);
|
||||
spv::Id scaled_result[4] = {};
|
||||
{
|
||||
spv::Id const_uint_1 = builder_->makeUintConstant(1);
|
||||
uint32_t result_remaining_components =
|
||||
used_result_nonzero_components;
|
||||
uint32_t result_component_index;
|
||||
while (xe::bit_scan_forward(result_remaining_components,
|
||||
&result_component_index)) {
|
||||
result_remaining_components &=
|
||||
~(UINT32_C(1) << result_component_index);
|
||||
spv::Id scale_bits = builder_->createTriOp(
|
||||
spv::OpBitFieldUExtract, type_uint_,
|
||||
integer_scale_bits_packed,
|
||||
builder_->makeUintConstant(result_component_index * 5),
|
||||
builder_->makeUintConstant(5));
|
||||
spv::Id scale_shift = builder_->createBinOp(
|
||||
spv::OpIAdd, type_uint_,
|
||||
builder_->createBinOp(spv::OpBitwiseAnd, type_uint_,
|
||||
scale_bits,
|
||||
builder_->makeUintConstant(0xF)),
|
||||
const_uint_1);
|
||||
scale_shift = builder_->createBinOp(
|
||||
spv::OpISub, type_uint_, scale_shift,
|
||||
builder_->createTriOp(spv::OpBitFieldUExtract, type_uint_,
|
||||
scale_bits,
|
||||
builder_->makeUintConstant(4),
|
||||
builder_->makeUintConstant(1)));
|
||||
spv::Id scale_uint = builder_->createBinOp(
|
||||
spv::OpISub, type_uint_,
|
||||
builder_->createBinOp(spv::OpShiftLeftLogical, type_uint_,
|
||||
const_uint_1, scale_shift),
|
||||
const_uint_1);
|
||||
scaled_result[result_component_index] =
|
||||
builder_->createNoContractionBinOp(
|
||||
spv::OpFMul, type_float_, result[result_component_index],
|
||||
builder_->createUnaryOp(spv::OpConvertUToF, type_float_,
|
||||
scale_uint));
|
||||
}
|
||||
}
|
||||
if_integer_scale.makeEndIf();
|
||||
// Keep the original result when the scale branch is skipped.
|
||||
uint32_t result_remaining_components = used_result_nonzero_components;
|
||||
uint32_t result_component_index;
|
||||
while (xe::bit_scan_forward(result_remaining_components,
|
||||
&result_component_index)) {
|
||||
result_remaining_components &=
|
||||
~(UINT32_C(1) << result_component_index);
|
||||
result[result_component_index] = if_integer_scale.createMergePhi(
|
||||
scaled_result[result_component_index],
|
||||
result[result_component_index]);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the exponent bias from the bits 13:18 of the fetch constant
|
||||
// word 3.
|
||||
spv::Id result_exponent_bias = builder_->createBinBuiltinCall(
|
||||
|
||||
@@ -340,6 +340,7 @@ void TextureCache::RequestTextures(uint32_t used_texture_mask) {
|
||||
TextureBinding& binding = texture_bindings_[index];
|
||||
xenos::xe_gpu_texture_fetch_t fetch = regs.GetTextureFetch(index);
|
||||
TextureKey old_key = binding.key;
|
||||
uint32_t old_integer_scale_bits = binding.integer_scale_bits;
|
||||
uint8_t old_swizzled_signs = binding.swizzled_signs;
|
||||
BindingInfoFromFetchConstant(fetch, binding.key, &binding.swizzled_signs);
|
||||
texture_bindings_in_sync_ |= index_bit;
|
||||
@@ -353,6 +354,9 @@ void TextureCache::RequestTextures(uint32_t used_texture_mask) {
|
||||
uint32_t old_host_swizzle = binding.host_swizzle;
|
||||
binding.host_swizzle =
|
||||
GuestToHostSwizzle(fetch.swizzle, GetHostFormatSwizzle(binding.key));
|
||||
binding.integer_scale_bits =
|
||||
GetIntegerScaleBits(fetch.format, fetch.num_format,
|
||||
binding.host_swizzle, binding.swizzled_signs);
|
||||
|
||||
// Check if need to load the unsigned and the signed versions of the texture
|
||||
// (if the format is emulated with different host bit representations for
|
||||
@@ -366,7 +370,8 @@ void TextureCache::RequestTextures(uint32_t used_texture_mask) {
|
||||
texture_util::IsAnySignNotSigned(binding.swizzled_signs);
|
||||
bool any_sign_is_signed =
|
||||
texture_util::IsAnySignSigned(binding.swizzled_signs);
|
||||
if (key_changed || binding.host_swizzle != old_host_swizzle ||
|
||||
if (key_changed || binding.integer_scale_bits != old_integer_scale_bits ||
|
||||
binding.host_swizzle != old_host_swizzle ||
|
||||
any_sign_is_not_signed != any_sign_was_not_signed ||
|
||||
any_sign_is_signed != any_sign_was_signed) {
|
||||
bindings_changed |= index_bit;
|
||||
@@ -670,6 +675,50 @@ TextureCache::Texture* TextureCache::FindOrCreateTexture(TextureKey key) {
|
||||
texture->LogAction("Created");
|
||||
return texture;
|
||||
}
|
||||
|
||||
// Packs the integer scale the fetch shader reads from the system constant to
|
||||
// undo the host sampler's normalization - the guest wants e.g. [0, 255], not
|
||||
// [0, 1]. 5 bits per output component: bits 0:3 = width - 1, bit 4 = signed.
|
||||
// The scale lands after swizzling, so each output lane walks the host swizzle
|
||||
// back to its source component's width; constant (0/1) lanes, gamma, and
|
||||
// non-fixed formats have nothing to rescale and stay 0.
|
||||
uint32_t TextureCache::GetIntegerScaleBits(xenos::TextureFormat guest_format,
|
||||
uint32_t num_format,
|
||||
uint32_t host_swizzle,
|
||||
uint8_t swizzled_signs) {
|
||||
// num_format 0 is the normalized/fractional fetch - nothing to rescale.
|
||||
const FormatInfo& format_info = *FormatInfo::Get(guest_format);
|
||||
uint32_t scale_bits = 0;
|
||||
|
||||
if (!num_format || !format_info.fixed) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < 4; ++i) {
|
||||
uint32_t source_component = (host_swizzle >> (i * 3)) & 0b111;
|
||||
if (source_component >= xenos::XE_GPU_TEXTURE_SWIZZLE_0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
xenos::TextureSign sign =
|
||||
xenos::TextureSign((swizzled_signs >> (i * 2)) & 0b11);
|
||||
|
||||
uint8_t width = format_info.component_bits[source_component];
|
||||
if (!width || width > 16 || sign == xenos::TextureSign::kGamma) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t component_scale = uint32_t(width - 1);
|
||||
if (sign == xenos::TextureSign::kSigned) {
|
||||
component_scale |= UINT32_C(1) << 4;
|
||||
}
|
||||
|
||||
scale_bits |= component_scale << (i * 5);
|
||||
}
|
||||
|
||||
return scale_bits;
|
||||
}
|
||||
|
||||
void TextureCache::LoadTexturesData(Texture** textures, uint32_t n_textures) {
|
||||
assert_true(n_textures <= 64);
|
||||
if (n_textures < 2) {
|
||||
|
||||
@@ -137,6 +137,11 @@ class TextureCache {
|
||||
GetValidTextureBinding(fetch_constant_index);
|
||||
return binding ? binding->swizzled_signs : kSwizzledSignsUnsigned;
|
||||
}
|
||||
uint32_t GetActiveIntegerScaleBits(uint32_t fetch_constant_index) const {
|
||||
const TextureBinding* binding =
|
||||
GetValidTextureBinding(fetch_constant_index);
|
||||
return binding ? binding->integer_scale_bits : 0;
|
||||
}
|
||||
bool IsActiveTextureResolutionScaled(uint32_t fetch_constant_index) const {
|
||||
const TextureBinding* binding =
|
||||
GetValidTextureBinding(fetch_constant_index);
|
||||
@@ -500,6 +505,8 @@ class TextureCache {
|
||||
|
||||
struct TextureBinding {
|
||||
TextureKey key;
|
||||
// Packed integer scale, 5 bits per component.
|
||||
uint32_t integer_scale_bits;
|
||||
// Destination swizzle merged with guest to host format swizzle.
|
||||
uint32_t host_swizzle;
|
||||
// Packed TextureSign values, 2 bit per each component, with guest-side
|
||||
@@ -577,6 +584,12 @@ class TextureCache {
|
||||
assert_true(load_shader_index < kLoadShaderCount);
|
||||
return load_shader_info_[load_shader_index];
|
||||
}
|
||||
// Integer num_format on fixed textures. Returns the packed scale used by the
|
||||
// shader to restore guest integer units from normalized host samples.
|
||||
static uint32_t GetIntegerScaleBits(xenos::TextureFormat guest_format,
|
||||
uint32_t num_format,
|
||||
uint32_t host_swizzle,
|
||||
uint8_t swizzled_signs);
|
||||
bool LoadTextureData(Texture& texture);
|
||||
void LoadTexturesData(Texture** textures, uint32_t n_textures);
|
||||
// Writes the texture data (for base, mips or both - but not neither) from the
|
||||
|
||||
@@ -195,6 +195,8 @@ struct FormatInfo {
|
||||
const uint32_t block_width;
|
||||
const uint32_t block_height;
|
||||
const uint32_t bits_per_pixel;
|
||||
const uint8_t component_bits[4];
|
||||
const bool fixed;
|
||||
|
||||
uint32_t bytes_per_block() const {
|
||||
return block_width * block_height * bits_per_pixel / 8;
|
||||
|
||||
@@ -16,8 +16,8 @@ namespace gpu {
|
||||
|
||||
using namespace xe::gpu::xenos;
|
||||
|
||||
#define FORMAT_INFO(texture_format, format, block_width, block_height, bits_per_pixel) \
|
||||
{xenos::TextureFormat::texture_format, FormatType::format, block_width, block_height, bits_per_pixel}
|
||||
#define FORMAT_INFO(texture_format, format, block_width, block_height, bits_per_pixel, component_bits_0, component_bits_1, component_bits_2, component_bits_3, fixed) \
|
||||
{xenos::TextureFormat::texture_format, FormatType::format, block_width, block_height, bits_per_pixel, {component_bits_0, component_bits_1, component_bits_2, component_bits_3}, fixed}
|
||||
const FormatInfo* FormatInfo::Get(uint32_t gpu_format) {
|
||||
static constexpr FormatInfo format_infos[64] = {
|
||||
#include "texture_info_formats.inl"
|
||||
@@ -43,7 +43,7 @@ constexpr unsigned char GetShift(unsigned pow) {
|
||||
this means we can use a boolean table that also acts as a sparse indexer ( popcnt preceding bits to get index) and shift and mask a 32 bit word to get the shift
|
||||
*/
|
||||
unsigned char FormatInfo::GetWidthShift(uint32_t gpu_format) {
|
||||
#define FORMAT_INFO(texture_format, format, block_width, block_height, bits_per_pixel) GetShift(block_width)
|
||||
#define FORMAT_INFO(texture_format, format, block_width, block_height, bits_per_pixel, component_bits_0, component_bits_1, component_bits_2, component_bits_3, fixed) GetShift(block_width)
|
||||
alignas(XE_HOST_CACHE_LINE_SIZE)
|
||||
constexpr unsigned char wshift_table[64] = {
|
||||
#include "texture_info_formats.inl"
|
||||
@@ -53,7 +53,7 @@ unsigned char FormatInfo::GetWidthShift(uint32_t gpu_format) {
|
||||
return wshift_table[gpu_format];
|
||||
}
|
||||
unsigned char FormatInfo::GetHeightShift(uint32_t gpu_format) {
|
||||
#define FORMAT_INFO(texture_format, format, block_width, block_height, bits_per_pixel) GetShift(block_height)
|
||||
#define FORMAT_INFO(texture_format, format, block_width, block_height, bits_per_pixel, component_bits_0, component_bits_1, component_bits_2, component_bits_3, fixed) GetShift(block_height)
|
||||
alignas(XE_HOST_CACHE_LINE_SIZE)
|
||||
constexpr unsigned char hshift_table[64] = {
|
||||
#include "texture_info_formats.inl"
|
||||
|
||||
@@ -1,64 +1,77 @@
|
||||
FORMAT_INFO(k_1_REVERSE, kUncompressed, 1, 1, 1),
|
||||
FORMAT_INFO(k_1, kUncompressed, 1, 1, 1),
|
||||
FORMAT_INFO(k_8, kResolvable, 1, 1, 8),
|
||||
FORMAT_INFO(k_1_5_5_5, kResolvable, 1, 1, 16),
|
||||
FORMAT_INFO(k_5_6_5, kResolvable, 1, 1, 16),
|
||||
FORMAT_INFO(k_6_5_5, kResolvable, 1, 1, 16),
|
||||
FORMAT_INFO(k_8_8_8_8, kResolvable, 1, 1, 32),
|
||||
FORMAT_INFO(k_2_10_10_10, kResolvable, 1, 1, 32),
|
||||
FORMAT_INFO(k_8_A, kResolvable, 1, 1, 8),
|
||||
FORMAT_INFO(k_8_B, kResolvable, 1, 1, 8),
|
||||
FORMAT_INFO(k_8_8, kResolvable, 1, 1, 16),
|
||||
FORMAT_INFO(k_Cr_Y1_Cb_Y0_REP, kCompressed, 2, 1, 16),
|
||||
FORMAT_INFO(k_Y1_Cr_Y0_Cb_REP, kCompressed, 2, 1, 16),
|
||||
FORMAT_INFO(k_16_16_EDRAM, kUncompressed, 1, 1, 32),
|
||||
FORMAT_INFO(k_8_8_8_8_A, kResolvable, 1, 1, 32),
|
||||
FORMAT_INFO(k_4_4_4_4, kResolvable, 1, 1, 16),
|
||||
FORMAT_INFO(k_10_11_11, kResolvable, 1, 1, 32),
|
||||
FORMAT_INFO(k_11_11_10, kResolvable, 1, 1, 32),
|
||||
FORMAT_INFO(k_DXT1, kCompressed, 4, 4, 4),
|
||||
FORMAT_INFO(k_DXT2_3, kCompressed, 4, 4, 8),
|
||||
FORMAT_INFO(k_DXT4_5, kCompressed, 4, 4, 8),
|
||||
FORMAT_INFO(k_16_16_16_16_EDRAM, kUncompressed, 1, 1, 64),
|
||||
FORMAT_INFO(k_24_8, kUncompressed, 1, 1, 32),
|
||||
FORMAT_INFO(k_24_8_FLOAT, kUncompressed, 1, 1, 32),
|
||||
FORMAT_INFO(k_16, kResolvable, 1, 1, 16),
|
||||
FORMAT_INFO(k_16_16, kResolvable, 1, 1, 32),
|
||||
FORMAT_INFO(k_16_16_16_16, kResolvable, 1, 1, 64),
|
||||
FORMAT_INFO(k_16_EXPAND, kUncompressed, 1, 1, 16),
|
||||
FORMAT_INFO(k_16_16_EXPAND, kUncompressed, 1, 1, 32),
|
||||
FORMAT_INFO(k_16_16_16_16_EXPAND, kUncompressed, 1, 1, 64),
|
||||
FORMAT_INFO(k_16_FLOAT, kResolvable, 1, 1, 16),
|
||||
FORMAT_INFO(k_16_16_FLOAT, kResolvable, 1, 1, 32),
|
||||
FORMAT_INFO(k_16_16_16_16_FLOAT, kResolvable, 1, 1, 64),
|
||||
FORMAT_INFO(k_32, kUncompressed, 1, 1, 32),
|
||||
FORMAT_INFO(k_32_32, kUncompressed, 1, 1, 64),
|
||||
FORMAT_INFO(k_32_32_32_32, kUncompressed, 1, 1, 128),
|
||||
FORMAT_INFO(k_32_FLOAT, kResolvable, 1, 1, 32),
|
||||
FORMAT_INFO(k_32_32_FLOAT, kResolvable, 1, 1, 64),
|
||||
FORMAT_INFO(k_32_32_32_32_FLOAT, kResolvable, 1, 1, 128),
|
||||
FORMAT_INFO(k_32_AS_8, kCompressed, 4, 1, 8),
|
||||
FORMAT_INFO(k_32_AS_8_8, kCompressed, 2, 1, 16),
|
||||
FORMAT_INFO(k_16_MPEG, kUncompressed, 1, 1, 16),
|
||||
FORMAT_INFO(k_16_16_MPEG, kUncompressed, 1, 1, 32),
|
||||
FORMAT_INFO(k_8_INTERLACED, kUncompressed, 1, 1, 8),
|
||||
FORMAT_INFO(k_32_AS_8_INTERLACED, kCompressed, 4, 1, 8),
|
||||
FORMAT_INFO(k_32_AS_8_8_INTERLACED, kCompressed, 1, 1, 16),
|
||||
FORMAT_INFO(k_16_INTERLACED, kUncompressed, 1, 1, 16),
|
||||
FORMAT_INFO(k_16_MPEG_INTERLACED, kUncompressed, 1, 1, 16),
|
||||
FORMAT_INFO(k_16_16_MPEG_INTERLACED, kUncompressed, 1, 1, 32),
|
||||
FORMAT_INFO(k_DXN, kCompressed, 4, 4, 8),
|
||||
FORMAT_INFO(k_8_8_8_8_AS_16_16_16_16, kResolvable, 1, 1, 32),
|
||||
FORMAT_INFO(k_DXT1_AS_16_16_16_16, kCompressed, 4, 4, 4),
|
||||
FORMAT_INFO(k_DXT2_3_AS_16_16_16_16, kCompressed, 4, 4, 8),
|
||||
FORMAT_INFO(k_DXT4_5_AS_16_16_16_16, kCompressed, 4, 4, 8),
|
||||
FORMAT_INFO(k_2_10_10_10_AS_16_16_16_16, kResolvable, 1, 1, 32),
|
||||
FORMAT_INFO(k_10_11_11_AS_16_16_16_16, kResolvable, 1, 1, 32),
|
||||
FORMAT_INFO(k_11_11_10_AS_16_16_16_16, kResolvable, 1, 1, 32),
|
||||
FORMAT_INFO(k_32_32_32_FLOAT, kUncompressed, 1, 1, 96),
|
||||
FORMAT_INFO(k_DXT3A, kCompressed, 4, 4, 4),
|
||||
FORMAT_INFO(k_DXT5A, kCompressed, 4, 4, 4),
|
||||
FORMAT_INFO(k_CTX1, kCompressed, 4, 4, 4),
|
||||
FORMAT_INFO(k_DXT3A_AS_1_1_1_1, kCompressed, 4, 4, 4),
|
||||
FORMAT_INFO(k_8_8_8_8_GAMMA_EDRAM, kUncompressed, 1, 1, 32),
|
||||
FORMAT_INFO(k_2_10_10_10_FLOAT_EDRAM, kUncompressed, 1, 1, 32),
|
||||
FORMAT_INFO(k_1_REVERSE, kUncompressed, 1, 1, 1, 0, 0, 0, 0, false),
|
||||
FORMAT_INFO(k_1, kUncompressed, 1, 1, 1, 0, 0, 0, 0, false),
|
||||
FORMAT_INFO(k_8, kResolvable, 1, 1, 8, 8, 0, 0, 0, true),
|
||||
FORMAT_INFO(k_1_5_5_5, kResolvable, 1, 1, 16, 5, 5, 5, 1, true),
|
||||
FORMAT_INFO(k_5_6_5, kResolvable, 1, 1, 16, 5, 6, 5, 0, true),
|
||||
FORMAT_INFO(k_6_5_5, kResolvable, 1, 1, 16, 5, 5, 6, 0, true),
|
||||
FORMAT_INFO(k_8_8_8_8, kResolvable, 1, 1, 32, 8, 8, 8, 8, true),
|
||||
FORMAT_INFO(k_2_10_10_10, kResolvable, 1, 1, 32, 10, 10, 10, 2, true),
|
||||
FORMAT_INFO(k_8_A, kResolvable, 1, 1, 8, 8, 0, 0, 0, true),
|
||||
FORMAT_INFO(k_8_B, kResolvable, 1, 1, 8, 8, 0, 0, 0, true),
|
||||
FORMAT_INFO(k_8_8, kResolvable, 1, 1, 16, 8, 8, 0, 0, true),
|
||||
FORMAT_INFO(k_Cr_Y1_Cb_Y0_REP, kCompressed, 2, 1, 16, 0, 0, 0, 0, false),
|
||||
FORMAT_INFO(k_Y1_Cr_Y0_Cb_REP, kCompressed, 2, 1, 16, 0, 0, 0, 0, false),
|
||||
FORMAT_INFO(k_16_16_EDRAM, kUncompressed, 1, 1, 32, 16, 16, 0, 0, true),
|
||||
FORMAT_INFO(k_8_8_8_8_A, kResolvable, 1, 1, 32, 8, 8, 8, 8, true),
|
||||
FORMAT_INFO(k_4_4_4_4, kResolvable, 1, 1, 16, 4, 4, 4, 4, true),
|
||||
FORMAT_INFO(k_10_11_11, kResolvable, 1, 1, 32, 11, 11, 10, 0, true),
|
||||
FORMAT_INFO(k_11_11_10, kResolvable, 1, 1, 32, 10, 11, 11, 0, true),
|
||||
FORMAT_INFO(k_DXT1, kCompressed, 4, 4, 4, 0, 0, 0, 0, false),
|
||||
FORMAT_INFO(k_DXT2_3, kCompressed, 4, 4, 8, 0, 0, 0, 0, false),
|
||||
FORMAT_INFO(k_DXT4_5, kCompressed, 4, 4, 8, 0, 0, 0, 0, false),
|
||||
FORMAT_INFO(k_16_16_16_16_EDRAM, kUncompressed, 1, 1, 64, 16, 16, 16, 16,
|
||||
true),
|
||||
FORMAT_INFO(k_24_8, kUncompressed, 1, 1, 32, 0, 0, 0, 0, false),
|
||||
FORMAT_INFO(k_24_8_FLOAT, kUncompressed, 1, 1, 32, 0, 0, 0, 0, false),
|
||||
FORMAT_INFO(k_16, kResolvable, 1, 1, 16, 16, 0, 0, 0, true),
|
||||
FORMAT_INFO(k_16_16, kResolvable, 1, 1, 32, 16, 16, 0, 0, true),
|
||||
FORMAT_INFO(k_16_16_16_16, kResolvable, 1, 1, 64, 16, 16, 16, 16, true),
|
||||
FORMAT_INFO(k_16_EXPAND, kUncompressed, 1, 1, 16, 0, 0, 0, 0, false),
|
||||
FORMAT_INFO(k_16_16_EXPAND, kUncompressed, 1, 1, 32, 0, 0, 0, 0, false),
|
||||
FORMAT_INFO(k_16_16_16_16_EXPAND, kUncompressed, 1, 1, 64, 0, 0, 0, 0,
|
||||
false),
|
||||
FORMAT_INFO(k_16_FLOAT, kResolvable, 1, 1, 16, 0, 0, 0, 0, false),
|
||||
FORMAT_INFO(k_16_16_FLOAT, kResolvable, 1, 1, 32, 0, 0, 0, 0, false),
|
||||
FORMAT_INFO(k_16_16_16_16_FLOAT, kResolvable, 1, 1, 64, 0, 0, 0, 0, false),
|
||||
FORMAT_INFO(k_32, kUncompressed, 1, 1, 32, 0, 0, 0, 0, false),
|
||||
FORMAT_INFO(k_32_32, kUncompressed, 1, 1, 64, 0, 0, 0, 0, false),
|
||||
FORMAT_INFO(k_32_32_32_32, kUncompressed, 1, 1, 128, 0, 0, 0, 0, false),
|
||||
FORMAT_INFO(k_32_FLOAT, kResolvable, 1, 1, 32, 0, 0, 0, 0, false),
|
||||
FORMAT_INFO(k_32_32_FLOAT, kResolvable, 1, 1, 64, 0, 0, 0, 0, false),
|
||||
FORMAT_INFO(k_32_32_32_32_FLOAT, kResolvable, 1, 1, 128, 0, 0, 0, 0, false),
|
||||
FORMAT_INFO(k_32_AS_8, kCompressed, 4, 1, 8, 0, 0, 0, 0, false),
|
||||
FORMAT_INFO(k_32_AS_8_8, kCompressed, 2, 1, 16, 0, 0, 0, 0, false),
|
||||
FORMAT_INFO(k_16_MPEG, kUncompressed, 1, 1, 16, 0, 0, 0, 0, false),
|
||||
FORMAT_INFO(k_16_16_MPEG, kUncompressed, 1, 1, 32, 0, 0, 0, 0, false),
|
||||
FORMAT_INFO(k_8_INTERLACED, kUncompressed, 1, 1, 8, 0, 0, 0, 0, false),
|
||||
FORMAT_INFO(k_32_AS_8_INTERLACED, kCompressed, 4, 1, 8, 0, 0, 0, 0, false),
|
||||
FORMAT_INFO(k_32_AS_8_8_INTERLACED, kCompressed, 1, 1, 16, 0, 0, 0, 0,
|
||||
false),
|
||||
FORMAT_INFO(k_16_INTERLACED, kUncompressed, 1, 1, 16, 0, 0, 0, 0, false),
|
||||
FORMAT_INFO(k_16_MPEG_INTERLACED, kUncompressed, 1, 1, 16, 0, 0, 0, 0,
|
||||
false),
|
||||
FORMAT_INFO(k_16_16_MPEG_INTERLACED, kUncompressed, 1, 1, 32, 0, 0, 0, 0,
|
||||
false),
|
||||
FORMAT_INFO(k_DXN, kCompressed, 4, 4, 8, 8, 8, 0, 0, true),
|
||||
FORMAT_INFO(k_8_8_8_8_AS_16_16_16_16, kResolvable, 1, 1, 32, 8, 8, 8, 8,
|
||||
true),
|
||||
FORMAT_INFO(k_DXT1_AS_16_16_16_16, kCompressed, 4, 4, 4, 0, 0, 0, 0, false),
|
||||
FORMAT_INFO(k_DXT2_3_AS_16_16_16_16, kCompressed, 4, 4, 8, 0, 0, 0, 0,
|
||||
false),
|
||||
FORMAT_INFO(k_DXT4_5_AS_16_16_16_16, kCompressed, 4, 4, 8, 0, 0, 0, 0,
|
||||
false),
|
||||
FORMAT_INFO(k_2_10_10_10_AS_16_16_16_16, kResolvable, 1, 1, 32, 10, 10, 10,
|
||||
2, true),
|
||||
FORMAT_INFO(k_10_11_11_AS_16_16_16_16, kResolvable, 1, 1, 32, 11, 11, 10, 0,
|
||||
true),
|
||||
FORMAT_INFO(k_11_11_10_AS_16_16_16_16, kResolvable, 1, 1, 32, 10, 11, 11, 0,
|
||||
true),
|
||||
FORMAT_INFO(k_32_32_32_FLOAT, kUncompressed, 1, 1, 96, 0, 0, 0, 0, false),
|
||||
FORMAT_INFO(k_DXT3A, kCompressed, 4, 4, 4, 8, 0, 0, 0, true),
|
||||
FORMAT_INFO(k_DXT5A, kCompressed, 4, 4, 4, 8, 0, 0, 0, true),
|
||||
FORMAT_INFO(k_CTX1, kCompressed, 4, 4, 4, 8, 8, 0, 0, true),
|
||||
FORMAT_INFO(k_DXT3A_AS_1_1_1_1, kCompressed, 4, 4, 4, 0, 0, 0, 0, false),
|
||||
FORMAT_INFO(k_8_8_8_8_GAMMA_EDRAM, kUncompressed, 1, 1, 32, 8, 8, 8, 8,
|
||||
true),
|
||||
FORMAT_INFO(k_2_10_10_10_FLOAT_EDRAM, kUncompressed, 1, 1, 32, 0, 0, 0, 0,
|
||||
false),
|
||||
|
||||
@@ -4705,6 +4705,12 @@ void VulkanCommandProcessor::UpdateSystemConstantValues(
|
||||
(texture_signs_uint & texture_signs_mask) != texture_signs_shifted;
|
||||
texture_signs_uint =
|
||||
(texture_signs_uint & ~texture_signs_mask) | texture_signs_shifted;
|
||||
uint32_t texture_integer_scale_bits =
|
||||
texture_cache_->GetActiveIntegerScaleBits(texture_index);
|
||||
dirty |= system_constants_.texture_integer_scale_bits[texture_index] !=
|
||||
texture_integer_scale_bits;
|
||||
system_constants_.texture_integer_scale_bits[texture_index] =
|
||||
texture_integer_scale_bits;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -219,10 +219,10 @@ class VulkanTextureCache final : public TextureCache {
|
||||
struct HostFormat {
|
||||
LoadShaderIndex load_shader;
|
||||
// Do NOT add integer formats to this - they are not filterable, can only be
|
||||
// read with ImageFetch, not ImageSample! If any game is seen using
|
||||
// num_format 1 for fixed-point formats (for floating-point, it's normally
|
||||
// set to 1 though), add a constant buffer containing multipliers for the
|
||||
// textures and multiplication to the tfetch implementation.
|
||||
// read with ImageFetch, not ImageSample! Games that fetch fixed-point
|
||||
// formats are handled after sampling by scaling the normalized host value
|
||||
// back to the guest integer range (see GetIntegerScaleBits). Keep these as
|
||||
// sampled float/normalized views.
|
||||
VkFormat format;
|
||||
// Whether the format is block-compressed on the host (the host block size
|
||||
// matches the guest format block size in this case), and isn't decompressed
|
||||
|
||||
Reference in New Issue
Block a user