Account existing pinned textures across preload retries

Bound reservations against total retained residency, skip decoding pinned images, and report complete repeated loads truthfully. Cover cap reductions and publication retry in both formats. Tracking: beads-eio.3.386.
This commit is contained in:
Matthew Stanley
2026-10-24 08:31:16 -07:00
parent 338bf846cb
commit c3ff07814c
5 changed files with 71 additions and 15 deletions
+6 -3
View File
@@ -196,14 +196,17 @@ void hd_texture_pack_set_decode_budget(HdTexturePack* pack,
* preloaded (over the cap, unreadable, malformed, a worker failure) are left
* to the normal asynchronous, budgeted decode path; nothing is marked failed
* by the preload. Returns 1 when every image was preloaded, 0 for a partial
* preload (see out), -1 on error. */
* preload (see out), -1 on error. Repeated calls count already pinned images
* without reading/decoding them again; max_bytes caps the pack's total pinned
* residency. A cap smaller than existing residency is rejected with -1 and
* the existing residency reported; it does not discard live pinned images. */
typedef struct HdTexturePreloadResult {
int requested; /* unambiguous replacements considered */
int loaded; /* decoded and pinned */
int loaded; /* resident and pinned, including earlier calls */
int over_budget; /* skipped: the aggregate max_bytes was reached */
int failed; /* unreadable, malformed, too large, or worker error */
int worker_errors; /* of failed: a decode that threw (caught) */
size_t bytes; /* decoded RGBA pinned */
size_t bytes; /* total decoded RGBA pinned by this pack */
} HdTexturePreloadResult;
int hd_texture_pack_preload(HdTexturePack* pack, size_t max_bytes,
unsigned max_threads, HdTexturePreloadResult* out);
+26 -7
View File
@@ -1165,17 +1165,37 @@ int duck_texture_pack_preload(DuckTexturePack* p, size_t max_bytes, unsigned max
if (!p) return -1;
try {
namespace tp = psx_texpreload;
Worker& w = p->worker;
std::vector<uint64_t> ids;
for (size_t i = 0; i < p->entries.size(); ++i)
if (!p->entries[i].ambiguous) ids.push_back(i + 1);
size_t per_image = 0;
{
std::lock_guard<std::mutex> lock(p->worker.mutex);
per_image = p->worker.budget;
std::lock_guard<std::mutex> lock(w.mutex);
per_image = w.budget;
/* The cap and result describe the pack's total pinned residency,
* including earlier successful calls or publication prefixes. */
for (const auto& item : w.decoded)
if (item.second.pinned && item.second.image)
r.bytes += item.second.image->rgba.size();
for (size_t i = 0; i < p->entries.size(); ++i) {
if (p->entries[i].ambiguous) continue;
++r.requested;
const auto it = w.decoded.find(i + 1);
if (it != w.decoded.end() && it->second.pinned) ++r.loaded;
}
if (r.bytes > max_bytes) {
r.failed = r.requested - r.loaded;
if (out) *out = r;
return -1; /* A smaller cap cannot discard live pinned data. */
}
for (size_t i = 0; i < p->entries.size(); ++i) {
if (p->entries[i].ambiguous) continue;
const auto it = w.decoded.find(i + 1);
if (it == w.decoded.end() || !it->second.pinned) ids.push_back(i + 1);
}
}
std::vector<std::shared_ptr<Image>> images(ids.size());
std::vector<unsigned char> status(ids.size(), tp::kPending);
tp::Budget budget(max_bytes);
tp::Budget budget(max_bytes - r.bytes);
r.worker_errors = static_cast<int>(tp::run(ids.size(), max_threads, [&](size_t k) {
status[k] = tp::kFailed;
const std::string& path = p->entries[ids[k] - 1].path;
@@ -1202,8 +1222,7 @@ int duck_texture_pack_preload(DuckTexturePack* p, size_t max_bytes, unsigned max
reservation.keep = true;
status[k] = tp::kLoaded;
}));
r.requested = static_cast<int>(ids.size());
Worker& w = p->worker; std::lock_guard<std::mutex> lock(w.mutex);
std::lock_guard<std::mutex> lock(w.mutex);
for (size_t k = 0; k < ids.size(); ++k) {
if (status[k] == tp::kOverBudget) { ++r.over_budget; continue; }
if (status[k] != tp::kLoaded) continue;
+20 -5
View File
@@ -1340,17 +1340,33 @@ int hd_texture_pack_preload(HdTexturePack* pack, size_t max_bytes, unsigned max_
try {
namespace tp = psx_texpreload;
std::vector<std::pair<uint64_t, std::string>> work_items;
for (const auto& entry : pack->entries)
if (!entry.second.ambiguous)
work_items.emplace_back(entry.first, entry.second.replacement_path);
size_t per_image = 0;
{
std::lock_guard<std::mutex> lock(pack->decode.mutex);
per_image = pack->decode.budget;
for (const auto& item : pack->decode.items)
if (item.second.pinned) r.bytes += item.second.bytes;
for (const auto& entry : pack->entries) {
if (entry.second.ambiguous) continue;
++r.requested;
const auto it = pack->decode.items.find(entry.first);
if (it != pack->decode.items.end() && it->second.pinned) ++r.loaded;
}
if (r.bytes > max_bytes) {
r.failed = r.requested - r.loaded;
if (out) *out = r;
return -1;
}
for (const auto& entry : pack->entries) {
if (entry.second.ambiguous) continue;
const auto it = pack->decode.items.find(entry.first);
if (it == pack->decode.items.end() || !it->second.pinned)
work_items.emplace_back(entry.first, entry.second.replacement_path);
}
}
std::vector<std::shared_ptr<DecodedImage>> images(work_items.size());
std::vector<unsigned char> status(work_items.size(), tp::kPending);
tp::Budget budget(max_bytes);
tp::Budget budget(max_bytes - r.bytes);
r.worker_errors = static_cast<int>(tp::run(work_items.size(), max_threads, [&](size_t k) {
status[k] = tp::kFailed;
std::ifstream input(work_items[k].second, std::ios::binary | std::ios::ate);
@@ -1388,7 +1404,6 @@ int hd_texture_pack_preload(HdTexturePack* pack, size_t max_bytes, unsigned max_
reservation.keep = true;
status[k] = tp::kLoaded;
}));
r.requested = static_cast<int>(work_items.size());
std::lock_guard<std::mutex> lock(pack->decode.mutex);
for (size_t k = 0; k < work_items.size(); ++k) {
if (status[k] == tp::kOverBudget) { ++r.over_budget; continue; }
@@ -130,6 +130,11 @@ void preload(const fs::path& root) {
pixels.width == 16,"preloaded image is resident without a decode request");
duck_texture_pixels_release(&pixels);
duck_texture_pack_set_decode_budget(pack.get(),1);
check(duck_texture_pack_preload(pack.get(),256,1,&r) == 1 && r.loaded == 1 &&
r.failed == 0 && r.bytes == 256 && r.worker_errors == 0,
"repeat counts existing residency without decoding under the reduced budget");
check(duck_texture_pack_preload(pack.get(),255,1,&r) == -1 && r.bytes == 256,
"a smaller lifetime cap rejects without hiding pinned residency");
check(duck_texture_pack_acquire_decoded(pack.get(),match.entry_id,&pixels) == HD_TEXTURE_LOOKUP_FOUND,
"a preloaded image survives budget eviction");
duck_texture_pixels_release(&pixels);
@@ -162,6 +167,9 @@ void preload(const fs::path& root) {
tp::fault_mode() = 0;
check(status == -1 && r.loaded == 1 && r.bytes == 16*4*4,
"publication failure still reports every retained byte");
check(duck_texture_pack_preload(pack.get(),256,1,&r) == 1 &&
r.loaded == 1 && r.failed == 0 && r.bytes == 256,
"publication retry accounts the retained prefix without decoding again");
}
{ /* Malformed image: failed, not pinned, async decode reports failure. */
std::ofstream(root/"replacements"/name,std::ios::binary|std::ios::trunc) << "not a png";
+11
View File
@@ -545,6 +545,14 @@ void test_bounded_preload(Fixture& fixture) {
(r.requested == 1 ? status == 1 : (status == 0 && r.failed >= 1)),
"malformed replacements are reported, not counted as preloaded");
hd_texture_pack_set_decode_budget(pack, 1);
HdTexturePreloadResult repeat{};
check(hd_texture_pack_preload(pack,64u*8u*4u,1,&repeat) >= 0 &&
repeat.loaded == 1 && repeat.bytes == 64u*8u*4u &&
repeat.failed == repeat.requested - 1,
"repeat counts pinned images without decoding them under a reduced budget");
check(hd_texture_pack_preload(pack,64u*8u*4u-1,1,&repeat) == -1 &&
repeat.bytes == 64u*8u*4u,
"a smaller lifetime cap rejects and reports retained residency");
check(hd_texture_pack_track_upload(pack, 10, 20, 8, 4,
fixture.upload_words.data(),
fixture.upload_words.size(), nullptr) == 1,
@@ -586,6 +594,9 @@ void test_bounded_preload(Fixture& fixture) {
tp::fault_mode() = 0;
check(status == -1 && r.loaded == 1 && r.bytes == 64u * 8u * 4u,
"publication failure still accounts the retained pinned image");
check(hd_texture_pack_preload(pack,64u*8u*4u,1,&r) >= 0 &&
r.loaded == 1 && r.bytes == 64u*8u*4u && r.failed == r.requested-1,
"publication retry accounts existing pinned residency");
hd_texture_pack_destroy(pack);
}
}