mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-29 08:41:20 +00:00
Fix DCC clears through aliased image formats
Clear DCC images using the requested view format. UNORM alpha 0xffff reads as NaN in a FLOAT view.
This commit is contained in:
1 parent
18a1e0fadc
commit
ea092a9472
4 files changed
+46
-17
No files matched your search
+10
-10
@@ -33,8 +33,8 @@ namespace {
|
|||||||
|
|
||||||
constexpr uint64_t NumFramesBeforeRemoval = 32;
|
constexpr uint64_t NumFramesBeforeRemoval = 32;
|
||||||
|
|
||||||
[[nodiscard]] bool DecodeDccClear(const TextureCache::ImageDesc& desc, vk::Format format,
|
[[nodiscard]] bool DecodeDccClear(const TextureCache::ImageDesc& desc, uint8_t code,
|
||||||
uint8_t code, vk::ClearColorValue& clear) {
|
vk::ClearColorValue& clear) {
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case 0x00:
|
case 0x00:
|
||||||
case 0x20:
|
case 0x20:
|
||||||
@@ -44,6 +44,7 @@ constexpr uint64_t NumFramesBeforeRemoval = 32;
|
|||||||
default: return false;
|
default: return false;
|
||||||
}
|
}
|
||||||
const auto& metadata = desc.info.metadata;
|
const auto& metadata = desc.info.metadata;
|
||||||
|
const auto format = desc.view_info.format;
|
||||||
if (code == 0x20) {
|
if (code == 0x20) {
|
||||||
// Clear-to-register is a color-buffer operation; the texture pipe cannot decode it.
|
// Clear-to-register is a color-buffer operation; the texture pipe cannot decode it.
|
||||||
return desc.type == TextureCache::BindingType::RenderTarget &&
|
return desc.type == TextureCache::BindingType::RenderTarget &&
|
||||||
@@ -1129,7 +1130,6 @@ void TextureCache::MaterializeDccClear(ImageId id, const ImageDesc& desc,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto range = desc.info.metadata.range;
|
const auto range = desc.info.metadata.range;
|
||||||
vk::Format format;
|
|
||||||
{
|
{
|
||||||
std::scoped_lock lock {m_lock};
|
std::scoped_lock lock {m_lock};
|
||||||
auto& image = m_slot_images[id];
|
auto& image = m_slot_images[id];
|
||||||
@@ -1139,7 +1139,6 @@ void TextureCache::MaterializeDccClear(ImageId id, const ImageDesc& desc,
|
|||||||
if (range.size == 0 || desc.info.resources.levels != 1 || image.info.resources.levels != 1) {
|
if (range.size == 0 || desc.info.resources.levels != 1 || image.info.resources.levels != 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
format = image.backing.format;
|
|
||||||
}
|
}
|
||||||
const auto layers = desc.info.TransferLayers();
|
const auto layers = desc.info.TransferLayers();
|
||||||
// These one-mip surfaces use complete 4 KiB DCC metadata blocks.
|
// These one-mip surfaces use complete 4 KiB DCC metadata blocks.
|
||||||
@@ -1169,7 +1168,7 @@ void TextureCache::MaterializeDccClear(ImageId id, const ImageDesc& desc,
|
|||||||
EXIT("TextureCache: failed to read DCC metadata backing\n");
|
EXIT("TextureCache: failed to read DCC metadata backing\n");
|
||||||
}
|
}
|
||||||
vk::ClearValue clear {};
|
vk::ClearValue clear {};
|
||||||
if (!DecodeDccClear(desc, format, code, clear.color)) {
|
if (!DecodeDccClear(desc, code, clear.color)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
std::vector<uint8_t> bytes(slice_size);
|
std::vector<uint8_t> bytes(slice_size);
|
||||||
@@ -1181,7 +1180,7 @@ void TextureCache::MaterializeDccClear(ImageId id, const ImageDesc& desc,
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::scoped_lock lock {m_lock};
|
std::scoped_lock lock {m_lock};
|
||||||
ClearImage(m_scheduler.Current(), id,
|
ClearImage(m_scheduler.Current(), id, view.format,
|
||||||
{vk::ImageAspectFlagBits::eColor, view.base_level, view.level_count,
|
{vk::ImageAspectFlagBits::eColor, view.base_level, view.level_count,
|
||||||
image_first + slice, 1}, clear);
|
image_first + slice, 1}, clear);
|
||||||
}
|
}
|
||||||
@@ -1526,12 +1525,12 @@ bool TextureCache::ClearImageFromBuffer(CommandBuffer& command, uint64_t address
|
|||||||
}
|
}
|
||||||
clear.depthStencil.stencil = stencil_clear;
|
clear.depthStencil.stencil = stencil_clear;
|
||||||
}
|
}
|
||||||
ClearImage(command, selected,
|
ClearImage(command, selected, image.backing.format,
|
||||||
{aspect, 0, image.info.resources.levels, 0, image.info.TransferLayers()}, clear);
|
{aspect, 0, image.info.resources.levels, 0, image.info.TransferLayers()}, clear);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextureCache::ClearImage(CommandBuffer& command, ImageId id,
|
void TextureCache::ClearImage(CommandBuffer& command, ImageId id, vk::Format format,
|
||||||
const vk::ImageSubresourceRange& range, const vk::ClearValue& clear) {
|
const vk::ImageSubresourceRange& range, const vk::ClearValue& clear) {
|
||||||
auto& image = m_slot_images[id];
|
auto& image = m_slot_images[id];
|
||||||
const auto aspects = image.info.IsDepth() ? ImageViewOps::DepthAspectMask(image.backing.format)
|
const auto aspects = image.info.IsDepth() ? ImageViewOps::DepthAspectMask(image.backing.format)
|
||||||
@@ -1556,11 +1555,12 @@ void TextureCache::ClearImage(CommandBuffer& command, ImageId id,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
command.EndRendering();
|
command.EndRendering();
|
||||||
if (image.info.IsVolume() && !full_image) {
|
// Transfer clears use the backing format; aliased clears must encode through their view.
|
||||||
|
if (format != image.backing.format || (image.info.IsVolume() && !full_image)) {
|
||||||
EXIT_NOT_IMPLEMENTED(range.aspectMask != vk::ImageAspectFlagBits::eColor ||
|
EXIT_NOT_IMPLEMENTED(range.aspectMask != vk::ImageAspectFlagBits::eColor ||
|
||||||
range.levelCount != 1);
|
range.levelCount != 1);
|
||||||
ImageViewInfo view {};
|
ImageViewInfo view {};
|
||||||
view.format = image.backing.format;
|
view.format = format;
|
||||||
view.type = range.layerCount == 1 ? vk::ImageViewType::e2D : vk::ImageViewType::e2DArray;
|
view.type = range.layerCount == 1 ? vk::ImageViewType::e2D : vk::ImageViewType::e2DArray;
|
||||||
view.base_level = range.baseMipLevel;
|
view.base_level = range.baseMipLevel;
|
||||||
view.base_layer = range.baseArrayLayer;
|
view.base_layer = range.baseArrayLayer;
|
||||||
|
|||||||
+2
-2
@@ -151,8 +151,8 @@ private:
|
|||||||
void DownloadDepth(Image& image, Buffer& destination, uint64_t destination_offset);
|
void DownloadDepth(Image& image, Buffer& destination, uint64_t destination_offset);
|
||||||
void CommitGpuWrite(Image& image);
|
void CommitGpuWrite(Image& image);
|
||||||
// Caller holds m_lock. Volume layer ranges select depth slices.
|
// Caller holds m_lock. Volume layer ranges select depth slices.
|
||||||
void ClearImage(CommandBuffer& command, ImageId id, const vk::ImageSubresourceRange& range,
|
void ClearImage(CommandBuffer& command, ImageId id, vk::Format format,
|
||||||
const vk::ClearValue& clear);
|
const vk::ImageSubresourceRange& range, const vk::ClearValue& clear);
|
||||||
void PrepareImageCopy(Image& image);
|
void PrepareImageCopy(Image& image);
|
||||||
void RefreshCopySource(ImageId id);
|
void RefreshCopySource(ImageId id);
|
||||||
[[nodiscard]] bool CopyD16(Image& destination, Image& source);
|
[[nodiscard]] bool CopyD16(Image& destination, Image& source);
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ bool RenderExecutor::TryConsumeComputeImageClear(const ShaderComputeInputInfo& i
|
|||||||
1, view.base_layer, view.layer_count};
|
1, view.base_layer, view.layer_count};
|
||||||
vk::ClearValue clear {};
|
vk::ClearValue clear {};
|
||||||
clear.depthStencil = vk::ClearDepthStencilValue {0.0f, fill.value};
|
clear.depthStencil = vk::ClearDepthStencilValue {0.0f, fill.value};
|
||||||
cache.ClearImage(command, binding.image_id, range, clear);
|
cache.ClearImage(command, binding.image_id, image.backing.format, range, clear);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
ShaderBufferResource descriptor;
|
ShaderBufferResource descriptor;
|
||||||
|
|||||||
@@ -219,7 +219,7 @@ struct TextureCacheTestAccess {
|
|||||||
const vk::ImageSubresourceRange &range,
|
const vk::ImageSubresourceRange &range,
|
||||||
const vk::ClearValue &clear) {
|
const vk::ClearValue &clear) {
|
||||||
auto lock = Lock(cache);
|
auto lock = Lock(cache);
|
||||||
cache.ClearImage(command, id, range, clear);
|
cache.ClearImage(command, id, cache.GetImage(id).backing.format, range, clear);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ConfigureGarbageCollection(TextureCache &cache,
|
static void ConfigureGarbageCollection(TextureCache &cache,
|
||||||
@@ -8568,10 +8568,12 @@ public:
|
|||||||
struct FillCase {
|
struct FillCase {
|
||||||
uint32_t fill;
|
uint32_t fill;
|
||||||
std::array<uint32_t, 2> texel;
|
std::array<uint32_t, 2> texel;
|
||||||
|
bool reuse_unorm = false;
|
||||||
};
|
};
|
||||||
constexpr std::array cases{
|
constexpr std::array cases{
|
||||||
FillCase{0x40404040u, {0, 0x3c000000u}},
|
FillCase{0x40404040u, {0, 0x3c000000u}},
|
||||||
FillCase{0x80808080u, {0x3c003c00u, 0x00003c00u}},
|
FillCase{0x80808080u, {0x3c003c00u, 0x00003c00u}},
|
||||||
|
FillCase{0x40404040u, {0, 0x3c000000u}, true},
|
||||||
};
|
};
|
||||||
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.
|
||||||
@@ -8658,6 +8660,21 @@ public:
|
|||||||
auto &texture_cache = resources.GetTextureCache();
|
auto &texture_cache = resources.GetTextureCache();
|
||||||
auto &executor = context.GetRenderExecutor();
|
auto &executor = context.GetRenderExecutor();
|
||||||
resources.MapMemory(base, allocation_size);
|
resources.MapMemory(base, allocation_size);
|
||||||
|
ImageId unorm_id{};
|
||||||
|
if (fill_case.reuse_unorm) {
|
||||||
|
const auto float_info = registers.GetRenderTarget(0).info;
|
||||||
|
auto unorm_info = float_info;
|
||||||
|
unorm_info.dcc_compression_enable = false;
|
||||||
|
unorm_info.channel_type = Prospero::ChannelType::kUNorm;
|
||||||
|
registers.SetColorInfo(0, unorm_info);
|
||||||
|
RenderColorInfo unorm{};
|
||||||
|
RenderExecutorTestAccess::ResolveRenderColorTarget(
|
||||||
|
executor, scheduler.Current(), unorm, 0);
|
||||||
|
unorm_id = unorm.image_id;
|
||||||
|
(void)texture_cache.FindRenderTarget(unorm_id, unorm.desc);
|
||||||
|
RenderExecutorTestAccess::ResetBindings(executor);
|
||||||
|
registers.SetColorInfo(0, float_info);
|
||||||
|
}
|
||||||
const auto fill_metadata = [&](uint32_t count, bool raw = false) {
|
const auto fill_metadata = [&](uint32_t count, bool raw = false) {
|
||||||
const auto *shader = &native_fill;
|
const auto *shader = &native_fill;
|
||||||
if (raw) {
|
if (raw) {
|
||||||
@@ -8687,6 +8704,13 @@ public:
|
|||||||
RenderDepthInfo no_depth{};
|
RenderDepthInfo no_depth{};
|
||||||
const auto rendering = RenderExecutorTestAccess::AcquireRenderTargets(
|
const auto rendering = RenderExecutorTestAccess::AcquireRenderTargets(
|
||||||
executor, scheduler.Current(), &color, 1, no_depth);
|
executor, scheduler.Current(), &color, 1, no_depth);
|
||||||
|
if (fill_case.reuse_unorm) {
|
||||||
|
Require(name, "FLOAT clear reuses UNORM image",
|
||||||
|
color.image_id == unorm_id &&
|
||||||
|
texture_cache.GetImage(unorm_id).backing.format ==
|
||||||
|
vk::Format::eR16G16B16A16Unorm,
|
||||||
|
"the aliased clear did not reuse its existing UNORM allocation");
|
||||||
|
}
|
||||||
Require(name, "fixed clear on a float target",
|
Require(name, "fixed clear on a float target",
|
||||||
color.image_id &&
|
color.image_id &&
|
||||||
color.desc.info.metadata.kind == ImageMetadataKind::Dcc &&
|
color.desc.info.metadata.kind == ImageMetadataKind::Dcc &&
|
||||||
@@ -8695,10 +8719,12 @@ public:
|
|||||||
!texture_cache.IsMeta(dcc_address),
|
!texture_cache.IsMeta(dcc_address),
|
||||||
"a DCC fixed clear code was not materialised on an RGBA16F "
|
"a DCC fixed clear code was not materialised on an RGBA16F "
|
||||||
"target");
|
"target");
|
||||||
|
const auto cleared = ReadCachedTexel(name, context, color.image_id);
|
||||||
Require(name, "GPU float clear value",
|
Require(name, "GPU float clear value",
|
||||||
ReadCachedTexel(name, context, color.image_id) ==
|
cleared ==
|
||||||
std::vector<u32>(fill_case.texel.begin(), fill_case.texel.end()),
|
std::vector<u32>(fill_case.texel.begin(), fill_case.texel.end()),
|
||||||
"RGBA16F native image did not receive the fixed clear value");
|
"expected " + Hex(fill_case.texel[0]) + ", " + Hex(fill_case.texel[1]) +
|
||||||
|
"; got " + Hex(cleared[0]) + ", " + Hex(cleared[1]));
|
||||||
context.GetBufferCache().ReadMemory(dcc_address, metadata_size.size, false);
|
context.GetBufferCache().ReadMemory(dcc_address, metadata_size.size, false);
|
||||||
std::vector<uint8_t> expanded_metadata(metadata_size.size);
|
std::vector<uint8_t> expanded_metadata(metadata_size.size);
|
||||||
Require(name, "expanded native metadata",
|
Require(name, "expanded native metadata",
|
||||||
@@ -8720,7 +8746,10 @@ public:
|
|||||||
texture_cache, scheduler.Current(), color.image_id,
|
texture_cache, scheduler.Current(), color.image_id,
|
||||||
{vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}, clear);
|
{vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}, clear);
|
||||||
};
|
};
|
||||||
const std::vector<u32> painted{0x00003c00u, 0x3c003c00u};
|
std::vector<u32> painted{0x00003c00u, 0x3c003c00u};
|
||||||
|
if (fill_case.reuse_unorm) {
|
||||||
|
painted = {0x0000ffffu, 0xffffffffu};
|
||||||
|
}
|
||||||
const auto retains_painted_texel = [&] {
|
const auto retains_painted_texel = [&] {
|
||||||
const auto texels = ReadCachedTexel(name, context, color.image_id, {}, {512, 256, 1});
|
const auto texels = ReadCachedTexel(name, context, color.image_id, {}, {512, 256, 1});
|
||||||
for (size_t i = 0; i < texels.size(); i += 2) {
|
for (size_t i = 0; i < texels.size(); i += 2) {
|
||||||
|
|||||||
Reference in new issue
Block a user