mirror of
https://github.com/Swordpdf/ProsperoEden.git
synced 2026-10-18 19:59:46 -05:00
* Texture cache: upload only the block rows guest writes changed in large textures A game streaming tiles into a 5120x5120 BC4 texture (17.8 MB) made every burst of writes re-upload the whole image, 4-8 ms of GPU-thread time each and up to nine times a frame. A guest write to a block-linear image of 1 MiB or more whose contents the GPU holds now untracks only the written 4 KiB pages; the next use unswizzles and copies the block rows that hold them (a block row is contiguous in guest memory and spans the full width), and tracks the pages again. Untracking the image for any other reason tracks them first, so the page counts balance. Single-level images, layers, tile width spacing and converted, accelerated or asynchronously decoded images keep the whole upload. dev-settings tex_partial=off restores it, tex_partial_kb=N sets the smallest image, and tex_partial_check=on compares each partial upload with a whole unswizzle. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Texture cache: check partial uploads against one copy of the image tex_partial_check read the rows and then the whole image separately, so a guest write in between counted as a mismatch. The check now reads the image once, takes the rows from that copy, and logs each mismatch (EDEN_TEX_PARTIAL_MISMATCH, first 50) with the image and rows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Texture cache: start partial-upload state over with each texture cache The page lists were keyed by ImageId index in a process-wide map, so a title started after returning to the menu found the previous title's entries under its own new images: it untracked pages of images that were never partial, re-tracked stale pages and called UntrackImage on untracked images (assert), unbalancing the page counts until guest memory read as unmapped and the game hung. The Vulkan texture cache clears the map when it is constructed; a partial write also requires a tracked image, and the fallback untracks only a tracked one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
59 lines
3.9 KiB
C++
59 lines
3.9 KiB
C++
// headless/texture_partial.h: an image that stayed Tracked uploads the rows of its written pages.
|
|
auto& partial = Eden::TexturePartial::images[image_id.index];
|
|
if (const auto pages = Eden::TexturePartial::End(device_memory, image_id.index); !pages.empty()) {
|
|
image.flags &= ~ImageFlagBits::CpuModified;
|
|
const auto ranges = Eden::TexturePartial::Ranges(pages, image.cpu_addr, image.guest_size_bytes);
|
|
// The check reads the whole image once and takes the rows from that same copy: the guest
|
|
// may write the image again (now tracked) while this runs.
|
|
const bool check = Eden::Performance::tex_partial_check.load(std::memory_order_relaxed);
|
|
if (check) {
|
|
swizzle_data_buffer.resize_destructive(image.guest_size_bytes);
|
|
gpu_memory->ReadBlockUnsafe(image.gpu_addr, swizzle_data_buffer.data(), image.guest_size_bytes);
|
|
}
|
|
const auto read = [this, &image, check](u32 offset, u8* data, u32 size) {
|
|
if (check) {
|
|
std::memcpy(data, swizzle_data_buffer.data() + offset, size);
|
|
} else {
|
|
gpu_memory->ReadBlockUnsafe(image.gpu_addr + offset, data, size);
|
|
}
|
|
};
|
|
if (const auto copies = UnswizzleImageRows(image.info, static_cast<u32>(image.guest_size_bytes),
|
|
ranges, read, unswizzle_data_buffer)) {
|
|
size_t bytes = 0;
|
|
for (const auto& copy : *copies) bytes += copy.buffer_size;
|
|
if (check && bytes != 0) {
|
|
const bool same = CheckImageRows(*gpu_memory, image.gpu_addr, image.info,
|
|
static_cast<u32>(image.guest_size_bytes),
|
|
std::span<const u8>(swizzle_data_buffer.data(), image.guest_size_bytes), *copies,
|
|
std::span<const u8>(unswizzle_data_buffer.data(), bytes));
|
|
Eden::Performance::tex_partial_checks.fetch_add(1, std::memory_order_relaxed);
|
|
if (!same && Eden::Performance::tex_partial_mismatches.fetch_add(1, std::memory_order_relaxed) < 50) {
|
|
std::printf("EDEN_TEX_PARTIAL_MISMATCH addr=%llx format=%u size=%ux%u levels=%u block=%u "
|
|
"pages=%zu ranges=%zu copies=%zu",
|
|
static_cast<unsigned long long>(image.gpu_addr), unsigned(image.info.format),
|
|
image.info.size.width, image.info.size.height, image.info.resources.levels,
|
|
image.info.block.height, pages.size(), ranges.size(), copies->size());
|
|
for (size_t i = 0; i < copies->size() && i < 8; ++i)
|
|
std::printf(" L%d:y%d+%u", (*copies)[i].image_subresource.base_level,
|
|
(*copies)[i].image_offset.y, (*copies)[i].image_extent.height);
|
|
std::printf("\n");
|
|
}
|
|
}
|
|
if (bytes != 0) {
|
|
auto staging = runtime.UploadStagingBuffer(bytes);
|
|
std::memcpy(staging.mapped_span.data(), unswizzle_data_buffer.data(), bytes);
|
|
image.UploadMemory(staging, *copies);
|
|
runtime.InsertUploadMemoryBarrier();
|
|
}
|
|
Eden::Performance::tex_partial_uploads.fetch_add(1, std::memory_order_relaxed);
|
|
Eden::Performance::tex_partial_bytes.fetch_add(bytes, std::memory_order_relaxed);
|
|
return;
|
|
}
|
|
// A layout the rows cannot follow: the whole upload below, which tracks the image again.
|
|
if (True(image.flags & ImageFlagBits::Tracked)) {
|
|
UntrackImage(image, image_id);
|
|
}
|
|
image.flags |= ImageFlagBits::CpuModified;
|
|
}
|
|
partial.uploaded = true;
|