mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-26 12:56:54 +01:00
Decode 16-bit color clears (TheCruZ/KytyPS5-GTA@b8dcd826)
This commit is contained in:
@@ -390,7 +390,28 @@ inline constexpr std::array<VideoOutFormatPolicy, 7> VIDEO_OUT_FORMAT_POLICIES {
|
|||||||
const auto encoded = static_cast<float>(value & 0xffu) / 255.0f;
|
const auto encoded = static_cast<float>(value & 0xffu) / 255.0f;
|
||||||
return encoded <= 0.04045f ? encoded / 12.92f : std::pow((encoded + 0.055f) / 1.055f, 2.4f);
|
return encoded <= 0.04045f ? encoded / 12.92f : std::pow((encoded + 0.055f) / 1.055f, 2.4f);
|
||||||
};
|
};
|
||||||
|
const auto float16 = [](uint32_t value) {
|
||||||
|
const auto sign = (value & 0x8000u) << 16u;
|
||||||
|
const auto exponent = (value >> 10u) & 0x1fu;
|
||||||
|
const auto mantissa = value & 0x3ffu;
|
||||||
|
if (exponent == 0) {
|
||||||
|
return std::copysign(static_cast<float>(mantissa) * 0x1p-24f,
|
||||||
|
std::bit_cast<float>(sign));
|
||||||
|
}
|
||||||
|
return std::bit_cast<float>(sign | ((exponent == 31 ? 255u : exponent + 112u) << 23u) |
|
||||||
|
(mantissa << 13u));
|
||||||
|
};
|
||||||
switch (format) {
|
switch (format) {
|
||||||
|
case vk::Format::eR16G16Sfloat:
|
||||||
|
next.float32[1] = float16(packed >> 16u);
|
||||||
|
[[fallthrough]];
|
||||||
|
case vk::Format::eR16Sfloat: next.float32[0] = float16(packed); break;
|
||||||
|
case vk::Format::eR16G16Unorm:
|
||||||
|
next.float32[1] = static_cast<float>(packed >> 16u) / 65535.0f;
|
||||||
|
[[fallthrough]];
|
||||||
|
case vk::Format::eR16Unorm:
|
||||||
|
next.float32[0] = static_cast<float>(packed & 0xffffu) / 65535.0f;
|
||||||
|
break;
|
||||||
// A single-plane float target carries its clear as raw float bits, the same encoding the
|
// A single-plane float target carries its clear as raw float bits, the same encoding the
|
||||||
// depth decoder below uses. Without this the clear is discarded and the target keeps stale
|
// depth decoder below uses. Without this the clear is discarded and the target keeps stale
|
||||||
// contents.
|
// contents.
|
||||||
@@ -443,6 +464,10 @@ inline constexpr std::array<VideoOutFormatPolicy, 7> VIDEO_OUT_FORMAT_POLICIES {
|
|||||||
// Keep this separate: the first register word alone cannot describe a 64-bit color.
|
// Keep this separate: the first register word alone cannot describe a 64-bit color.
|
||||||
[[nodiscard]] inline bool DecodeColorDwordFill(vk::Format format, uint32_t packed,
|
[[nodiscard]] inline bool DecodeColorDwordFill(vk::Format format, uint32_t packed,
|
||||||
vk::ClearColorValue& clear) {
|
vk::ClearColorValue& clear) {
|
||||||
|
if ((format == vk::Format::eR16Sfloat || format == vk::Format::eR16Unorm) &&
|
||||||
|
(packed & 0xffffu) != (packed >> 16u)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (format == vk::Format::eR16G16B16A16Sfloat) {
|
if (format == vk::Format::eR16G16B16A16Sfloat) {
|
||||||
if (packed != 0) {
|
if (packed != 0) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -10161,6 +10161,46 @@ public:
|
|||||||
|
|
||||||
void CheckRenderExecutorColorMetadataClear() {
|
void CheckRenderExecutorColorMetadataClear() {
|
||||||
constexpr const char *name = "RenderExecutorColorMetadataClear";
|
constexpr const char *name = "RenderExecutorColorMetadataClear";
|
||||||
|
struct PackedCase {
|
||||||
|
vk::Format format;
|
||||||
|
uint32_t word;
|
||||||
|
std::array<float, 4> channels;
|
||||||
|
};
|
||||||
|
const std::array packed_cases{
|
||||||
|
PackedCase{vk::Format::eR16Sfloat, 0x7bff7bffu, {65504.0f}},
|
||||||
|
PackedCase{vk::Format::eR16G16Sfloat, 0xbc003c00u, {1.0f, -1.0f}},
|
||||||
|
PackedCase{vk::Format::eR16Sfloat, 0xffff0001u, {0x1p-24f}},
|
||||||
|
PackedCase{vk::Format::eR16Sfloat, 0x00008001u, {-0x1p-24f}},
|
||||||
|
PackedCase{vk::Format::eR16Sfloat, 0x000003ffu, {1023.0f * 0x1p-24f}},
|
||||||
|
PackedCase{vk::Format::eR16Sfloat, 0x00000400u, {0x1p-14f}},
|
||||||
|
PackedCase{vk::Format::eR16G16Sfloat, 0x80000000u, {0.0f, -0.0f}},
|
||||||
|
PackedCase{vk::Format::eR16G16Sfloat, 0xfc007c00u,
|
||||||
|
{std::numeric_limits<float>::infinity(),
|
||||||
|
-std::numeric_limits<float>::infinity()}},
|
||||||
|
PackedCase{vk::Format::eR16Unorm, 0x0000ffffu, {1.0f}},
|
||||||
|
PackedCase{vk::Format::eR16G16Unorm, 0x0000ffffu, {1.0f, 0.0f}},
|
||||||
|
PackedCase{vk::Format::eR16G16Unorm, 0x80000001u,
|
||||||
|
{1.0f / 65535.0f, 32768.0f / 65535.0f}},
|
||||||
|
};
|
||||||
|
for (const auto &test : packed_cases) {
|
||||||
|
vk::ClearColorValue clear{};
|
||||||
|
Require(name, "packed 16-bit clear",
|
||||||
|
DecodePackedColorClear(test.format, test.word, clear) &&
|
||||||
|
std::bit_cast<std::array<uint32_t, 4>>(clear.float32) ==
|
||||||
|
std::bit_cast<std::array<uint32_t, 4>>(test.channels),
|
||||||
|
"16-bit clear lost channel order, sign, range or unused-channel zeros");
|
||||||
|
}
|
||||||
|
vk::ClearColorValue clear{};
|
||||||
|
Require(name, "half NaN clear",
|
||||||
|
DecodePackedColorClear(vk::Format::eR16Sfloat, 0x7e00u, clear) &&
|
||||||
|
std::isnan(clear.float32[0]),
|
||||||
|
"half NaN did not remain NaN");
|
||||||
|
for (const auto format : {vk::Format::eR16Sfloat, vk::Format::eR16Unorm}) {
|
||||||
|
Require(name, "R16 DWORD fill",
|
||||||
|
!DecodeColorDwordFill(format, 0xbc003c00u, clear) &&
|
||||||
|
DecodeColorDwordFill(format, 0x3c003c00u, clear),
|
||||||
|
"alternating R16 pixels were treated as a uniform clear");
|
||||||
|
}
|
||||||
constexpr uintptr_t base = 0x0000000204100000ull;
|
constexpr uintptr_t base = 0x0000000204100000ull;
|
||||||
constexpr uint64_t allocation_size = 0x200000;
|
constexpr uint64_t allocation_size = 0x200000;
|
||||||
constexpr uint64_t allocation_alignment = 0x10000;
|
constexpr uint64_t allocation_alignment = 0x10000;
|
||||||
@@ -10170,6 +10210,9 @@ public:
|
|||||||
bool reuse_unorm = false;
|
bool reuse_unorm = false;
|
||||||
bool cmask = false;
|
bool cmask = false;
|
||||||
bool reuse_mips = false;
|
bool reuse_mips = false;
|
||||||
|
Prospero::ChannelLayout layout = Prospero::ChannelLayout::k16_16_16_16;
|
||||||
|
Prospero::ChannelType type = Prospero::ChannelType::kFloat;
|
||||||
|
uint32_t clear_word = 0;
|
||||||
};
|
};
|
||||||
constexpr std::array cases{
|
constexpr std::array cases{
|
||||||
FillCase{0x40404040u, {0, 0x3c000000u}},
|
FillCase{0x40404040u, {0, 0x3c000000u}},
|
||||||
@@ -10177,6 +10220,16 @@ public:
|
|||||||
FillCase{0x40404040u, {0, 0x3c000000u}, true},
|
FillCase{0x40404040u, {0, 0x3c000000u}, true},
|
||||||
FillCase{0, {0x804020ffu, 0}, false, true},
|
FillCase{0, {0x804020ffu, 0}, false, true},
|
||||||
FillCase{0x40404040u, {0, 0x3c000000u}, false, false, true},
|
FillCase{0x40404040u, {0, 0x3c000000u}, false, false, true},
|
||||||
|
FillCase{.fill = 0x20202020u, .texel = {0x7bff7bffu},
|
||||||
|
.layout = Prospero::ChannelLayout::k16, .clear_word = 0x7bff7bffu},
|
||||||
|
FillCase{.fill = 0x20202020u, .texel = {0xbc003c00u},
|
||||||
|
.layout = Prospero::ChannelLayout::k16_16, .clear_word = 0xbc003c00u},
|
||||||
|
FillCase{.fill = 0x20202020u, .texel = {0x80008000u},
|
||||||
|
.layout = Prospero::ChannelLayout::k16,
|
||||||
|
.type = Prospero::ChannelType::kUNorm, .clear_word = 0x80008000u},
|
||||||
|
FillCase{.fill = 0x20202020u, .texel = {0x0000ffffu},
|
||||||
|
.layout = Prospero::ChannelLayout::k16_16,
|
||||||
|
.type = Prospero::ChannelType::kUNorm, .clear_word = 0x0000ffffu},
|
||||||
};
|
};
|
||||||
EnsureRuntimeContext();
|
EnsureRuntimeContext();
|
||||||
// Astro's generic metadata fill, through S_ENDPGM; trailing debug data is omitted.
|
// Astro's generic metadata fill, through S_ENDPGM; trailing debug data is omitted.
|
||||||
@@ -10214,10 +10267,6 @@ public:
|
|||||||
{.type = Prospero::ShaderBinaryType::kCs,
|
{.type = Prospero::ShaderBinaryType::kCs,
|
||||||
.code_size_bytes = sizeof(native_fill)});
|
.code_size_bytes = sizeof(native_fill)});
|
||||||
}
|
}
|
||||||
TileSizeAlign dcc_size{};
|
|
||||||
(void)TileGetDccSize(512, 256, 1, 8, 1,
|
|
||||||
Prospero::TileMode::kRenderTarget, dcc_size, 0);
|
|
||||||
|
|
||||||
int64_t direct_offset = -1;
|
int64_t direct_offset = -1;
|
||||||
Require(name, "direct allocation",
|
Require(name, "direct allocation",
|
||||||
Libs::LibKernel::Memory::KernelAllocateDirectMemory(
|
Libs::LibKernel::Memory::KernelAllocateDirectMemory(
|
||||||
@@ -10234,11 +10283,19 @@ public:
|
|||||||
std::memset(mapped, 0, allocation_size);
|
std::memset(mapped, 0, allocation_size);
|
||||||
|
|
||||||
for (const auto &fill_case : cases) {
|
for (const auto &fill_case : cases) {
|
||||||
|
const auto layout = fill_case.cmask ? Prospero::ChannelLayout::k8_8_8_8 : fill_case.layout;
|
||||||
|
const auto type = fill_case.cmask ? Prospero::ChannelType::kUNorm : fill_case.type;
|
||||||
|
const auto bytes_per_pixel = TextureGetRenderTargetFormat(
|
||||||
|
layout, type, Prospero::ChannelOrder::kStandard).bytes_per_element;
|
||||||
|
const uint32_t probe_width = bytes_per_pixel == 2 ? 2 : 1;
|
||||||
|
TileSizeAlign dcc_size{};
|
||||||
|
(void)TileGetDccSize(512, 256, 1, bytes_per_pixel, 1,
|
||||||
|
Prospero::TileMode::kRenderTarget, dcc_size, 0);
|
||||||
const uint64_t dcc_address = base + (fill_case.reuse_mips ? 0x160000 : 0x100000);
|
const uint64_t dcc_address = base + (fill_case.reuse_mips ? 0x160000 : 0x100000);
|
||||||
const uint32_t selected_layer = fill_case.cmask ? 1 : 0;
|
const uint32_t selected_layer = fill_case.cmask ? 1 : 0;
|
||||||
const uint64_t metadata_size = fill_case.cmask ? 0x2000 : dcc_size.size;
|
const uint64_t metadata_size = fill_case.cmask ? 0x2000 : dcc_size.size;
|
||||||
const std::vector<u32> expected(
|
const std::vector<u32> expected(
|
||||||
fill_case.texel.begin(), fill_case.texel.begin() + (fill_case.cmask ? 1 : 2));
|
fill_case.texel.begin(), fill_case.texel.begin() + std::max(bytes_per_pixel / 4u, 1u));
|
||||||
RenderContext context(m_runtime_context);
|
RenderContext context(m_runtime_context);
|
||||||
HW::Context registers{};
|
HW::Context registers{};
|
||||||
HW::UserConfig user_config{};
|
HW::UserConfig user_config{};
|
||||||
@@ -10251,10 +10308,8 @@ public:
|
|||||||
registers.SetColorInfo(
|
registers.SetColorInfo(
|
||||||
0, {.cmask_fast_clear_enable = !fill_case.reuse_unorm,
|
0, {.cmask_fast_clear_enable = !fill_case.reuse_unorm,
|
||||||
.dcc_compression_enable = !fill_case.cmask,
|
.dcc_compression_enable = !fill_case.cmask,
|
||||||
.format = fill_case.cmask ? Prospero::ChannelLayout::k8_8_8_8
|
.format = layout,
|
||||||
: Prospero::ChannelLayout::k16_16_16_16,
|
.channel_type = type,
|
||||||
.channel_type = fill_case.cmask ? Prospero::ChannelType::kUNorm
|
|
||||||
: Prospero::ChannelType::kFloat,
|
|
||||||
.channel_order = Prospero::ChannelOrder::kStandard});
|
.channel_order = Prospero::ChannelOrder::kStandard});
|
||||||
registers.SetColorView(0, {.base_array_slice_index = selected_layer,
|
registers.SetColorView(0, {.base_array_slice_index = selected_layer,
|
||||||
.last_array_slice_index = selected_layer});
|
.last_array_slice_index = selected_layer});
|
||||||
@@ -10265,7 +10320,7 @@ public:
|
|||||||
.metadata_pipe_aligned = true});
|
.metadata_pipe_aligned = true});
|
||||||
registers.SetColorDccAddr(0, {.addr = dcc_address});
|
registers.SetColorDccAddr(0, {.addr = dcc_address});
|
||||||
registers.SetColorCmask(0, {.addr = dcc_address});
|
registers.SetColorCmask(0, {.addr = dcc_address});
|
||||||
registers.SetColorClearWord0(0, {.word0 = fill_case.cmask ? expected[0] : 0});
|
registers.SetColorClearWord0(0, {.word0 = fill_case.cmask ? expected[0] : fill_case.clear_word});
|
||||||
registers.SetColorClearWord1(0, {.word1 = 0});
|
registers.SetColorClearWord1(0, {.word1 = 0});
|
||||||
registers.SetRenderTargetMask(0x0f);
|
registers.SetRenderTargetMask(0x0f);
|
||||||
scheduler.Begin(registers, user_config, shaders);
|
scheduler.Begin(registers, user_config, shaders);
|
||||||
@@ -10354,12 +10409,13 @@ public:
|
|||||||
!texture_cache.IsMeta(dcc_address),
|
!texture_cache.IsMeta(dcc_address),
|
||||||
"color metadata did not retain its type and full native allocation");
|
"color metadata did not retain its type and full native allocation");
|
||||||
const auto read_texel = [&] {
|
const auto read_texel = [&] {
|
||||||
return ReadCachedTexel(name, context, color.image_id, {}, {1, 1, 1}, selected_layer);
|
return ReadCachedTexel(name, context, color.image_id, {}, {probe_width, 1, 1}, selected_layer);
|
||||||
};
|
};
|
||||||
Require(name, "GPU color clear value",
|
Require(name, "GPU color clear value",
|
||||||
read_texel() == expected &&
|
read_texel() == expected &&
|
||||||
ReadCachedTexel(name, context, color.image_id, {511, 255, 0},
|
ReadCachedTexel(name, context, color.image_id,
|
||||||
{1, 1, 1}, selected_layer) == expected,
|
{static_cast<int32_t>(512 - probe_width), 255, 0},
|
||||||
|
{probe_width, 1, 1}, selected_layer) == expected,
|
||||||
"native metadata did not materialize the expected color");
|
"native metadata did not materialize the expected color");
|
||||||
if (fill_case.reuse_mips) {
|
if (fill_case.reuse_mips) {
|
||||||
const auto &image = texture_cache.GetImage(color.image_id);
|
const auto &image = texture_cache.GetImage(color.image_id);
|
||||||
@@ -10405,6 +10461,9 @@ public:
|
|||||||
painted = {0x0000ffffu, 0xffffffffu};
|
painted = {0x0000ffffu, 0xffffffffu};
|
||||||
} else if (fill_case.cmask) {
|
} else if (fill_case.cmask) {
|
||||||
painted = {0xffff00ffu};
|
painted = {0xffff00ffu};
|
||||||
|
} else if (bytes_per_pixel <= 4) {
|
||||||
|
const uint32_t one = type == Prospero::ChannelType::kFloat ? 0x3c00u : 0xffffu;
|
||||||
|
painted = {bytes_per_pixel == 2 ? one | (one << 16u) : one};
|
||||||
}
|
}
|
||||||
const auto retains_painted_texel = [&] {
|
const auto retains_painted_texel = [&] {
|
||||||
const auto texels = ReadCachedTexel(name, context, color.image_id, {},
|
const auto texels = ReadCachedTexel(name, context, color.image_id, {},
|
||||||
|
|||||||
Reference in New Issue
Block a user