fix(agc): copy a depth surface's plane through the texture's padded linear slices

The plane copy laid its staging buffer out as tight slices while the
detiler writes the guest's linear layout, which pads rows and slices: a
32x32 D16 cube in a 64 KiB swizzle has 16384-byte slices, not 2048, and a
128x128 one has 512-byte rows and 64 KiB slices. Each region in record
now takes the layer's LinearLayerOffset plus the mip's linear offset and
its pitch as the row length, for both the slices copied from a live
depth surface and the ones detiled from guest memory. The staging buffer
grows to the padded size, keeping the buffer it replaces alive for
copies already recorded, and a mismatch says which geometry it saw.
This commit is contained in:
Gustavo Ariel Gamarra Rojas authored and Adrià Franch committed 2026-10-14 19:21:26 +02:00
1 parent e154f56585
commit 6b480cc2be
1 file changed
+18 -3
@@ -316,7 +316,8 @@ public:
allocation.memoryTypeIndex = context.MemoryType(requirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
Check(context.Function<PFN_vkAllocateMemory>("vkAllocateMemory")(context.device, &allocation, nullptr, &memory), "vkAllocateMemory depth plane copy");
Check(context.Function<PFN_vkBindImageMemory>("vkBindImageMemory")(context.device, image, memory, 0), "vkBindImageMemory depth plane copy");
staging = std::make_unique<DeviceBuffer>(context, static_cast<std::size_t>(sliceBytes()) * layers, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
stagingSize = static_cast<std::size_t>(sliceBytes()) * layers;
staging = std::make_unique<DeviceBuffer>(context, stagingSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
} catch (...) {
release();
throw;
@@ -329,7 +330,17 @@ public:
std::shared_ptr<Texture> Refresh(std::span<const DepthPlaneSlice> slices, const GuestTextureResource& resource, VkComponentMapping components, VkImageViewType viewType, std::uint32_t baseLayer) {
Require(slices.size() == layers, "depth plane copy slices do not match its layers");
const auto geometry = DescribeSurface(resource);
Require(geometry.layers == layers && geometry.sliceLinearBytes == sliceBytes() && !geometry.mips.empty(), "depth plane copy geometry does not match its layers");
if (geometry.layers != layers || geometry.sliceLinearBytes < sliceBytes() || geometry.mips.empty()) {
char text[320];
std::snprintf(text, sizeof(text), "AGC graphics: depth plane copy geometry does not match its layers (copy %ux%u, %u layers, %llu bytes a slice; texture %ux%u guest format %u, dimension %d, tile mode %u, %u layers, %llu bytes a slice, %zu mips)", extent.width, extent.height, layers, static_cast<unsigned long long>(sliceBytes()), resource.width, resource.height, resource.format, static_cast<int>(resource.dimension), resource.tileMode, geometry.layers, static_cast<unsigned long long>(geometry.sliceLinearBytes), geometry.mips.size());
throw std::runtime_error(text);
}
const auto stagingBytes = static_cast<std::size_t>(geometry.sliceLinearBytes) * layers;
if (stagingBytes > stagingSize) {
retiredStaging.push_back(std::move(staging));
staging = std::make_unique<DeviceBuffer>(context, stagingBytes, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
stagingSize = stagingBytes;
}
std::vector<std::shared_ptr<Buffer>> uploads;
std::vector<std::uint32_t> uploadLayers;
std::vector<std::uint32_t> copiedLayers;
@@ -382,6 +393,8 @@ private:
VkImage image = VK_NULL_HANDLE;
VkDeviceMemory memory = VK_NULL_HANDLE;
std::unique_ptr<DeviceBuffer> staging;
std::size_t stagingSize = 0;
std::vector<std::unique_ptr<DeviceBuffer>> retiredStaging;
std::map<std::array<std::uint32_t, 6>, std::shared_ptr<Texture>> textures;
std::vector<GuestLayer> guestLayers;
std::vector<DepthPlaneSlice> surfaceLayers;
@@ -399,9 +412,11 @@ private:
const auto barrier = context.Resolved(&DeviceFunctions::cmdPipelineBarrier, "vkCmdPipelineBarrier");
RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT);
std::vector<VkBufferImageCopy> regions;
const auto& mip = geometry.mips.front();
const auto region = [&](std::uint32_t layer, VkImageAspectFlags aspectMask, std::uint32_t imageLayer) {
VkBufferImageCopy copy{};
copy.bufferOffset = sliceBytes() * layer;
copy.bufferOffset = geometry.LinearLayerOffset(layer) + mip.linearOffset;
copy.bufferRowLength = mip.pitchBytes / (format == VK_FORMAT_D32_SFLOAT ? 4u : 2u);
copy.imageSubresource = {aspectMask, 0, imageLayer, 1};
copy.imageExtent = {extent.width, extent.height, 1};
return copy;