Compare commits

...
2 Commits
Author SHA1 Message Date
b2c9ec7609 renderer: decode R8, R8G8 and R16_UINT color register clears (#1893)
DecodePackedColorClear had no case for R8_UNORM/UINT, R8G8_UNORM/UINT and
R16_UINT, so a CMASK fast clear of such a render target was dropped and the
target kept its old contents. UFC 6 (PPSA23566) fast-clears an R8_UINT
region map this way every frame and then writes only part of it; a capture
showed the map never cleared. DWORD fills of these formats are decoded only
when every pixel in the word holds the same value.

Co-authored-by: 4wgycvn84p-afk <346802885+4wgycvn84p-afk@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 18:47:45 +01:00
herrMirtoandAndre Michi 99044c7c49 libs: provide NpEntitlementAccessPft state query used by FC27 (#1897)
Co-authored-by: Andre Michi <herrmirto@proton.me>
2026-10-25 18:38:26 +01:00
4 changed files with 72 additions and 1 deletions

No files matched your search

@@ -416,6 +416,20 @@ inline constexpr std::array<VideoOutFormatPolicy, 8> VIDEO_OUT_FORMAT_POLICIES {
case vk::Format::eR32Sfloat: next.float32[0] = std::bit_cast<float>(packed); break;
case vk::Format::eR32Uint: next.uint32[0] = packed; break;
case vk::Format::eR32Sint: next.int32[0] = static_cast<int32_t>(packed); break;
// Narrow targets hold their channels in the low bits of the first word. UFC 6 (PPSA23566)
// fast-clears an R8_UINT map through CMASK every frame and then writes only part of it;
// without these the clear was dropped and old contents piled up.
case vk::Format::eR8Uint: next.uint32[0] = packed & 0xffu; break;
case vk::Format::eR8Unorm: next.float32[0] = unorm8(packed); break;
case vk::Format::eR8G8Uint:
next.uint32[0] = packed & 0xffu;
next.uint32[1] = (packed >> 8u) & 0xffu;
break;
case vk::Format::eR8G8Unorm:
next.float32[0] = unorm8(packed);
next.float32[1] = unorm8(packed >> 8u);
break;
case vk::Format::eR16Uint: next.uint32[0] = packed & 0xffffu; break;
case vk::Format::eR8G8B8A8Srgb:
next.float32[0] = srgb8(packed);
next.float32[1] = srgb8(packed >> 8u);
@@ -462,10 +476,16 @@ inline constexpr std::array<VideoOutFormatPolicy, 8> VIDEO_OUT_FORMAT_POLICIES {
// Keep this separate: the first register word alone cannot describe a 64-bit color.
[[nodiscard]] inline bool DecodeColorDwordFill(vk::Format format, uint32_t packed,
vk::ClearColorValue& clear) {
if ((format == vk::Format::eR16Sfloat || format == vk::Format::eR16Unorm) &&
if ((format == vk::Format::eR16Sfloat || format == vk::Format::eR16Unorm ||
format == vk::Format::eR16Uint || format == vk::Format::eR8G8Unorm ||
format == vk::Format::eR8G8Uint) &&
(packed & 0xffffu) != (packed >> 16u)) {
return false;
}
if ((format == vk::Format::eR8Unorm || format == vk::Format::eR8Uint) &&
packed != (packed & 0xffu) * 0x01010101u) {
return false;
}
if (format == vk::Format::eR16G16B16A16Sfloat) {
if (packed != 0) {
return false;
+4
View File
@@ -21,6 +21,9 @@ namespace Libs {
namespace LibNpEntitlementAccess {
void InitNet_1_NpEntitlementAccess(Loader::SymbolDatabase* s);
}
namespace LibNpEntitlementAccessPft {
void InitNet_1_NpEntitlementAccessPft(Loader::SymbolDatabase* s);
}
namespace LibNpTrophy2 {
void InitNet_1_NpTrophy2(Loader::SymbolDatabase* s);
}
@@ -2846,6 +2849,7 @@ LIB_DEFINE(InitNet_1) {
LibNpManager::InitNet_1_NpManager(s);
LibNpSessionSignaling::InitNet_1_NpSessionSignaling(s);
LibNpEntitlementAccess::InitNet_1_NpEntitlementAccess(s);
LibNpEntitlementAccessPft::InitNet_1_NpEntitlementAccessPft(s);
LibNpAuth::InitNet_1_NpAuth(s);
LibNpTrophy2::InitNet_1_NpTrophy2(s);
LibNpUniversalDataSystem::InitNet_1_NpUniversalDataSystem(s);
+22
View File
@@ -513,4 +513,26 @@ LIB_DEFINE(InitNet_1_NpEntitlementAccess) {
} // namespace LibNpEntitlementAccess
namespace LibNpEntitlementAccessPft {
LIB_VERSION("NpEntitlementAccessPft", 1, "NpEntitlementAccess", 1, 1);
// Unknown function; return state 2 for the FC27 investigation.
static int KYTY_SYSV_ABI NpEntitlementAccessPftUnknowneDXKe9FndlE(uint32_t* state) {
PRINT_NAME();
if (state == nullptr) {
return LibNpEntitlementAccess::NP_ENTITLEMENT_ACCESS_ERROR_PARAMETER;
}
*state = 2;
return 0;
}
LIB_DEFINE(InitNet_1_NpEntitlementAccessPft) {
LIB_FUNC("eDXKe9FndlE", NpEntitlementAccessPftUnknowneDXKe9FndlE);
}
} // namespace LibNpEntitlementAccessPft
} // namespace Libs
+25
View File
@@ -12274,6 +12274,8 @@ public:
PackedCase{vk::Format::eR16G16Unorm, 0x0000ffffu, {1.0f, 0.0f}},
PackedCase{vk::Format::eR16G16Unorm, 0x80000001u,
{1.0f / 65535.0f, 32768.0f / 65535.0f}},
PackedCase{vk::Format::eR8Unorm, 0xffffff80u, {128.0f / 255.0f}},
PackedCase{vk::Format::eR8G8Unorm, 0xffff40ffu, {1.0f, 64.0f / 255.0f}},
};
for (const auto &test : packed_cases) {
vk::ClearColorValue clear{};
@@ -12300,6 +12302,29 @@ public:
DecodeColorDwordFill(format, 0x3c003c00u, clear),
"alternating R16 pixels were treated as a uniform clear");
}
struct UintCase {
vk::Format format;
uint64_t word;
std::array<uint32_t, 4> channels;
};
for (const auto &test : {UintCase{vk::Format::eR8Uint, 0xffffff05u, {5u}},
UintCase{vk::Format::eR8G8Uint, 0xffff0a05u, {5u, 10u}},
UintCase{vk::Format::eR16Uint, 0xffff1234u, {0x1234u}}}) {
Require(name, "packed unsigned color clear",
DecodePackedColorClear(test.format, test.word, clear) &&
std::bit_cast<std::array<uint32_t, 4>>(clear.uint32) == test.channels,
"narrow unsigned clear lost a channel or kept bits of the next one");
}
Require(name, "R8 DWORD fill",
!DecodeColorDwordFill(vk::Format::eR8Uint, 0x01020304u, clear) &&
DecodeColorDwordFill(vk::Format::eR8Uint, 0x07070707u, clear) &&
clear.uint32[0] == 7u,
"differing R8 pixels were treated as a uniform clear");
Require(name, "RG8 DWORD fill",
!DecodeColorDwordFill(vk::Format::eR8G8Unorm, 0x00ff00feu, clear) &&
DecodeColorDwordFill(vk::Format::eR8G8Unorm, 0x00ff00ffu, clear) &&
clear.float32[0] == 1.0f && clear.float32[1] == 0.0f,
"alternating RG8 pixels were treated as a uniform clear");
Require(name, "RG32 DWORD fill",
DecodeColorDwordFill(vk::Format::eR32G32Sfloat, 0x3f800000u, clear) &&
clear.float32[0] == 1.0f && clear.float32[1] == 1.0f,