mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 10:19:47 -04:00
Merge branch 'review/736-preload-repeat-20261024' into review/psx-prs-20261024
# Conflicts: # runtime/include/hd_texture_pack.h # runtime/src/duckstation_texture_pack.cpp # runtime/src/hd_texture_pack.cpp # runtime/tests/test_duckstation_texture_pack.cpp # runtime/tests/test_hd_texture_pack.cpp
This commit is contained in:
@@ -187,14 +187,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);
|
||||
|
||||
@@ -1142,17 +1142,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;
|
||||
@@ -1179,8 +1199,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;
|
||||
|
||||
@@ -1305,17 +1305,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);
|
||||
@@ -1353,7 +1369,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";
|
||||
|
||||
@@ -527,6 +527,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,
|
||||
@@ -568,6 +576,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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user