texture cache: convert D16<->D24/D32 depth copies instead of crashing (#1873)

gpu: convert guest D16 depth between native D16 and stenciled four-byte host depth

Co-authored-by: nmzik <Nmzik@mail.ru>
This commit is contained in:
bigjjmoneyman
2026-10-27 05:41:22 -04:00
committed by GitHub
parent a5b3673a39
commit 0685bffb2f
2 changed files with 97 additions and 56 deletions
+29 -29
View File
@@ -488,16 +488,15 @@ void TextureCache::RefreshCopySource(ImageId id) {
} }
bool TextureCache::CopyD16(Image& destination, Image& source) { bool TextureCache::CopyD16(Image& destination, Image& source) {
const bool source_depth = source.info.IsDepth(); // Converts guest D16 between a four-byte host depth aspect and two-byte color or D16 depth.
const bool destination_depth = destination.info.IsDepth(); const bool source_wide = DepthAspectTransferBytes(source.backing.format) == sizeof(uint32_t);
if (source_depth == destination_depth) { if (source_wide == (DepthAspectTransferBytes(destination.backing.format) == sizeof(uint32_t))) {
return false; return false;
} }
auto& depth = source_depth ? source : destination; auto& depth = source_wide ? source : destination;
auto& color = source_depth ? destination : source; auto& narrow = source_wide ? destination : source;
const auto transfer_bytes = DepthAspectTransferBytes(depth.backing.format);
if (depth.info.bytes_per_block != sizeof(uint16_t) || if (depth.info.bytes_per_block != sizeof(uint16_t) ||
color.info.bytes_per_block != sizeof(uint16_t) || transfer_bytes != sizeof(uint32_t)) { narrow.info.bytes_per_block != sizeof(uint16_t)) {
return false; return false;
} }
EXIT_IF(source.backing.samples != 1 || destination.backing.samples != 1 || EXIT_IF(source.backing.samples != 1 || destination.backing.samples != 1 ||
@@ -507,12 +506,10 @@ bool TextureCache::CopyD16(Image& destination, Image& source) {
const auto layers = depth.info.resources.layers; const auto layers = depth.info.resources.layers;
const uint64_t depth_slice = const uint64_t depth_slice =
static_cast<uint64_t>(depth.info.pitch) * depth.info.extent.height * transfer_bytes; static_cast<uint64_t>(depth.info.pitch) * depth.info.extent.height * sizeof(uint32_t);
const uint64_t color_slice = const uint64_t narrow_slice =
static_cast<uint64_t>(color.info.pitch) * color.info.extent.height * sizeof(uint16_t); static_cast<uint64_t>(narrow.info.pitch) * narrow.info.extent.height * sizeof(uint16_t);
EXIT_IF(layers == 0 || depth_slice > UINT64_MAX / layers || color_slice > UINT64_MAX / layers); EXIT_IF(layers == 0 || depth_slice > UINT64_MAX / layers || narrow_slice > UINT64_MAX / layers);
const auto depth_size = depth_slice * layers;
const auto color_size = color_slice * layers;
const std::array depth_copies { const std::array depth_copies {
vk::BufferImageCopy {0, vk::BufferImageCopy {0,
depth.info.pitch, depth.info.pitch,
@@ -520,29 +517,31 @@ bool TextureCache::CopyD16(Image& destination, Image& source) {
{vk::ImageAspectFlagBits::eDepth, 0, 0, layers}, {vk::ImageAspectFlagBits::eDepth, 0, 0, layers},
{}, {},
depth.info.extent}}; depth.info.extent}};
const std::array color_copies { const std::array narrow_copies {
vk::BufferImageCopy {0, vk::BufferImageCopy {0,
color.info.pitch, narrow.info.pitch,
color.info.extent.height, narrow.info.extent.height,
{vk::ImageAspectFlagBits::eColor, 0, 0, layers}, {narrow.info.IsDepth() ? vk::ImageAspectFlagBits::eDepth
: vk::ImageAspectFlagBits::eColor,
0, 0, layers},
{}, {},
color.info.extent}}; narrow.info.extent}};
auto depth_buffer = m_tiler.GetScratchBuffer(depth_size); auto depth_buffer = m_tiler.GetScratchBuffer(depth_slice * layers);
auto color_buffer = m_tiler.GetScratchBuffer(color_size, depth_buffer.buffer); auto narrow_buffer = m_tiler.GetScratchBuffer(narrow_slice * layers, depth_buffer.buffer);
const TileManager::D16Layout promote_layout { const TileManager::D16Layout promote_layout {
.width = depth.info.extent.width, .width = depth.info.extent.width,
.height = depth.info.extent.height, .height = depth.info.extent.height,
.layers = layers, .layers = layers,
.source_row_stride = static_cast<uint64_t>(color.info.pitch) * sizeof(uint16_t), .source_row_stride = static_cast<uint64_t>(narrow.info.pitch) * sizeof(uint16_t),
.target_row_stride = static_cast<uint64_t>(depth.info.pitch) * transfer_bytes, .target_row_stride = static_cast<uint64_t>(depth.info.pitch) * sizeof(uint32_t),
.source_slice_stride = color_slice, .source_slice_stride = narrow_slice,
.target_slice_stride = depth_slice, .target_slice_stride = depth_slice,
}; };
const bool d32 = DepthAspectTransferFormat(depth.backing.format) == vk::Format::eD32Sfloat; const bool d32 = DepthAspectTransferFormat(depth.backing.format) == vk::Format::eD32Sfloat;
if (source_depth) { if (source_wide) {
source.Download(depth_copies, depth_buffer.buffer, depth_buffer.offset, depth_buffer.size); source.Download(depth_copies, depth_buffer.buffer, depth_buffer.offset, depth_buffer.size);
m_tiler.ConvertD16(depth_buffer, color_buffer, TileManager::D16Direction::Demote, d32, m_tiler.ConvertD16(depth_buffer, narrow_buffer, TileManager::D16Direction::Demote, d32,
{.width = promote_layout.width, {.width = promote_layout.width,
.height = promote_layout.height, .height = promote_layout.height,
.layers = promote_layout.layers, .layers = promote_layout.layers,
@@ -550,11 +549,12 @@ bool TextureCache::CopyD16(Image& destination, Image& source) {
.target_row_stride = promote_layout.source_row_stride, .target_row_stride = promote_layout.source_row_stride,
.source_slice_stride = promote_layout.target_slice_stride, .source_slice_stride = promote_layout.target_slice_stride,
.target_slice_stride = promote_layout.source_slice_stride}); .target_slice_stride = promote_layout.source_slice_stride});
destination.Upload(color_copies, color_buffer.buffer, color_buffer.offset, destination.Upload(narrow_copies, narrow_buffer.buffer, narrow_buffer.offset,
color_buffer.size); narrow_buffer.size);
} else { } else {
source.Download(color_copies, color_buffer.buffer, color_buffer.offset, color_buffer.size); source.Download(narrow_copies, narrow_buffer.buffer, narrow_buffer.offset,
m_tiler.ConvertD16(color_buffer, depth_buffer, TileManager::D16Direction::Promote, d32, narrow_buffer.size);
m_tiler.ConvertD16(narrow_buffer, depth_buffer, TileManager::D16Direction::Promote, d32,
promote_layout); promote_layout);
destination.Upload(depth_copies, depth_buffer.buffer, depth_buffer.offset, destination.Upload(depth_copies, depth_buffer.buffer, depth_buffer.offset,
depth_buffer.size); depth_buffer.size);
+61 -20
View File
@@ -9978,37 +9978,78 @@ public:
d16_storage_desc.view_info.usage = vk::ImageUsageFlagBits::eStorage; d16_storage_desc.view_info.usage = vk::ImageUsageFlagBits::eStorage;
const auto d16_storage_image = const auto d16_storage_image =
texture_cache.FindImage(d16_storage_desc, true); texture_cache.FindImage(d16_storage_desc, true);
auto d16_storage_readback = CreateHostBuffer( const auto read_texels = [&](ImageId id, vk::ImageAspectFlagBits aspect) {
name, sizeof(d16_fallback_values), auto readback = CreateHostBuffer(name, 4 * sizeof(u32),
vk::BufferUsageFlagBits::eTransferDst, std::vector<u32>(2, 0)); vk::BufferUsageFlagBits::eTransferDst,
auto &d16_storage_native = texture_cache.GetImage(d16_storage_image); std::vector<u32>(4, 0));
d16_storage_native.Transit(vk::ImageLayout::eTransferSrcOptimal, auto &image = texture_cache.GetImage(id);
image.Transit(vk::ImageLayout::eTransferSrcOptimal,
vk::AccessFlagBits2::eTransferRead, {}, vk::AccessFlagBits2::eTransferRead, {},
scheduler.Current().Handle()); scheduler.Current().Handle());
vk::BufferImageCopy d16_storage_copy{}; const vk::BufferImageCopy copy{0, 4, 0, {aspect, 0, 0, 1}, {}, {4, 1, 1}};
d16_storage_copy.bufferRowLength = 4;
d16_storage_copy.imageSubresource = {vk::ImageAspectFlagBits::eColor, 0,
0, 1};
d16_storage_copy.imageExtent = {4, 1, 1};
scheduler.Current().Handle().copyImageToBuffer( scheduler.Current().Handle().copyImageToBuffer(
d16_storage_native.backing.image, image.backing.image, vk::ImageLayout::eTransferSrcOptimal,
vk::ImageLayout::eTransferSrcOptimal, d16_storage_readback.buffer, 1, readback.buffer, 1, &copy);
&d16_storage_copy); HostReadBarrier(readback.buffer, readback.size,
HostReadBarrier(d16_storage_readback.buffer, d16_storage_readback.size,
vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eTransfer,
vk::AccessFlagBits::eTransferWrite); vk::AccessFlagBits::eTransferWrite);
scheduler.Finish(); scheduler.Finish();
const auto d16_storage_words = ReadBuffer(name, d16_storage_readback, 2); auto words = ReadBuffer(name, readback, 4);
std::array<uint16_t, 4> d16_storage_values{}; DestroyBuffer(&readback);
std::memcpy(d16_storage_values.data(), d16_storage_words.data(), return words;
sizeof(d16_storage_values)); };
const auto d16_storage_words =
read_texels(d16_storage_image, vk::ImageAspectFlagBits::eColor);
Require( Require(
name, "D16 fallback depth/storage copy", name, "D16 fallback depth/storage copy",
d16_storage_values == d16_fallback_values && std::memcmp(d16_storage_words.data(), d16_fallback_values.data(),
sizeof(d16_fallback_values)) == 0 &&
d16_storage_image != d16_depth_image && d16_storage_image != d16_depth_image &&
texture_cache.GetImage(d16_storage_image).IsGpuModified(), texture_cache.GetImage(d16_storage_image).IsGpuModified(),
"D16 fallback reinterpreted four-byte host depth as two-byte color"); "D16 fallback reinterpreted four-byte host depth as two-byte color");
DestroyBuffer(&d16_storage_readback);
// Dropping and re-adding the stencil plane keeps the D16 depth values.
constexpr uint64_t d16_stenciled_offset = 0x33e000;
constexpr std::array<uint16_t, 4> d16_stale_values{};
std::memcpy(memory + d16_stenciled_offset, d16_fallback_values.data(),
sizeof(d16_fallback_values));
auto d16_stenciled_desc = d16_depth_desc;
d16_stenciled_desc.info.data.address = base + d16_stenciled_offset;
d16_stenciled_desc.info.stencil.address = base + 0x33f000;
const auto d16_stenciled_image =
texture_cache.FindImage(d16_stenciled_desc);
(void)texture_cache.FindDepthTarget(d16_stenciled_image,
d16_stenciled_desc);
texture_cache.MarkGpuWritten(d16_stenciled_image);
// Stale guest bytes: the values below can come only from image copies.
Libs::LibKernel::Memory::WriteBacking(base + d16_stenciled_offset,
d16_stale_values.data(),
sizeof(d16_stale_values));
auto d16_native_desc = d16_stenciled_desc;
d16_native_desc.info.stencil = {};
d16_native_desc.info.pixel_format = vk::Format::eD16Unorm;
d16_native_desc.view_info.format = vk::Format::eD16Unorm;
d16_native_desc.view_info.aspect = vk::ImageAspectFlagBits::eDepth;
const auto d16_native_image = texture_cache.FindImage(d16_native_desc);
const auto d16_native_words =
read_texels(d16_native_image, vk::ImageAspectFlagBits::eDepth);
Require(name, "D16 fallback depth/native depth copy",
std::memcmp(d16_native_words.data(), d16_fallback_values.data(),
sizeof(d16_fallback_values)) == 0 &&
texture_cache.GetImage(d16_native_image).backing.format ==
vk::Format::eD16Unorm,
"D16 depth lost its values when its target dropped the stencil");
const auto d16_restenciled_image =
texture_cache.FindImage(d16_stenciled_desc);
const auto d16_restenciled_words =
read_texels(d16_restenciled_image, vk::ImageAspectFlagBits::eDepth);
bool d16_promoted = d16_restenciled_image != d16_native_image;
for (size_t i = 0; i < d16_fallback_values.size(); ++i) {
d16_promoted &= (d16_restenciled_words[i] & 0x00ffffffu) ==
EncodeD16AsD24(d16_fallback_values[i]);
}
Require(name, "D16 native depth/fallback depth copy", d16_promoted,
"D16 depth lost its values when its target regained the stencil");
if (!texture_cache.GetImage(combined_destination_image).IsGpuModified()) { if (!texture_cache.GetImage(combined_destination_image).IsGpuModified()) {
texture_cache.MarkGpuWritten(combined_destination_image); texture_cache.MarkGpuWritten(combined_destination_image);