mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
Clean up palette packing a bit
Rename a poorly-named attribute, and add a bunch of debug logging
This commit is contained in:
@@ -938,8 +938,10 @@ contained:;
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (palettes.size() > options.nbPalettes) {
|
if (palettes.size() > options.nbPalettes) {
|
||||||
// If the palette generation is wrong, other (dependee) operations are likely to be nonsensical, so fatal-error outright
|
// If the palette generation is wrong, other (dependee) operations are likely to be
|
||||||
fatal("Generated %zu palettes, over the maximum of %" PRIu8, palettes.size(), options.nbPalettes);
|
// nonsensical, so fatal-error outright
|
||||||
|
fatal("Generated %zu palettes, over the maximum of %" PRIu8, palettes.size(),
|
||||||
|
options.nbPalettes);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!options.palettes.empty()) {
|
if (!options.palettes.empty()) {
|
||||||
|
|||||||
@@ -39,14 +39,14 @@ namespace packing {
|
|||||||
* A reference to a proto-palette, and attached attributes for sorting purposes
|
* A reference to a proto-palette, and attached attributes for sorting purposes
|
||||||
*/
|
*/
|
||||||
struct ProtoPalAttrs {
|
struct ProtoPalAttrs {
|
||||||
size_t const palIndex;
|
size_t const protoPalIndex;
|
||||||
/**
|
/**
|
||||||
* Pages from which we are banned (to prevent infinite loops)
|
* Pages from which we are banned (to prevent infinite loops)
|
||||||
* This is dynamic because we wish not to hard-cap the amount of palettes
|
* This is dynamic because we wish not to hard-cap the amount of palettes
|
||||||
*/
|
*/
|
||||||
std::vector<bool> bannedPages;
|
std::vector<bool> bannedPages;
|
||||||
|
|
||||||
ProtoPalAttrs(size_t index) : palIndex(index) {}
|
ProtoPalAttrs(size_t index) : protoPalIndex(index) {}
|
||||||
bool isBannedFrom(size_t index) const {
|
bool isBannedFrom(size_t index) const {
|
||||||
return index < bannedPages.size() && bannedPages[index];
|
return index < bannedPages.size() && bannedPages[index];
|
||||||
}
|
}
|
||||||
@@ -169,7 +169,7 @@ private:
|
|||||||
static void addUniqueColors(std::unordered_set<uint16_t> &colors, Iter iter, Iter const &end,
|
static void addUniqueColors(std::unordered_set<uint16_t> &colors, Iter iter, Iter const &end,
|
||||||
std::vector<ProtoPalette> const &protoPals) {
|
std::vector<ProtoPalette> const &protoPals) {
|
||||||
for (; iter != end; ++iter) {
|
for (; iter != end; ++iter) {
|
||||||
ProtoPalette const &protoPal = protoPals[iter->palIndex];
|
ProtoPalette const &protoPal = protoPals[iter->protoPalIndex];
|
||||||
colors.insert(protoPal.begin(), protoPal.end());
|
colors.insert(protoPal.begin(), protoPal.end());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -216,7 +216,7 @@ public:
|
|||||||
// if the symbol is not found anywhere, so I'm assuming the paper is wrong.
|
// if the symbol is not found anywhere, so I'm assuming the paper is wrong.
|
||||||
relSize +=
|
relSize +=
|
||||||
1. / (1 + std::count_if(begin(), end(), [this, &color](ProtoPalAttrs const &attrs) {
|
1. / (1 + std::count_if(begin(), end(), [this, &color](ProtoPalAttrs const &attrs) {
|
||||||
ProtoPalette const &pal = (*_protoPals)[attrs.palIndex];
|
ProtoPalette const &pal = (*_protoPals)[attrs.protoPalIndex];
|
||||||
return std::find(pal.begin(), pal.end(), color) != pal.end();
|
return std::find(pal.begin(), pal.end(), color) != pal.end();
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -258,26 +258,32 @@ static void decant(std::vector<AssignedProtos> &assignments,
|
|||||||
// If the proto-palette is now empty, remove it
|
// If the proto-palette is now empty, remove it
|
||||||
// Doing this now reduces the number of iterations performed by later steps
|
// Doing this now reduces the number of iterations performed by later steps
|
||||||
// NB: order is intentionally preserved so as not to alter the "decantation"'s
|
// NB: order is intentionally preserved so as not to alter the "decantation"'s
|
||||||
// properties NB: this does mean that the first step might get empty palettes as its
|
// properties
|
||||||
// input! NB: this is safe to do because we go towards the beginning of the vector,
|
// NB: this does mean that the first step might get empty palettes as its input!
|
||||||
// thereby not invalidating our iteration (thus, iterators should not be used to drive
|
// NB: this is safe to do because we go towards the beginning of the vector, thereby not
|
||||||
// the outer loop)
|
// invalidating our iteration (thus, iterators should not be used to drivethe outer
|
||||||
|
// loop)
|
||||||
if (assignments[from].empty()) {
|
if (assignments[from].empty()) {
|
||||||
assignments.erase(assignments.begin() + from);
|
assignments.erase(assignments.begin() + from);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
options.verbosePrint(Options::VERB_DEBUG, "%zu palettes before decanting\n",
|
||||||
|
assignments.size());
|
||||||
|
|
||||||
// Decant on palettes
|
// Decant on palettes
|
||||||
decantOn([&protoPalettes](AssignedProtos &to, AssignedProtos &from) {
|
decantOn([&protoPalettes](AssignedProtos &to, AssignedProtos &from) {
|
||||||
// If the entire palettes can be merged, move all of `from`'s proto-palettes
|
// If the entire palettes can be merged, move all of `from`'s proto-palettes
|
||||||
if (to.combinedVolume(from.begin(), from.end(), protoPalettes) <= options.maxPalSize()) {
|
if (to.combinedVolume(from.begin(), from.end(), protoPalettes) <= options.maxPalSize()) {
|
||||||
for (ProtoPalAttrs &protoPal : from) {
|
for (ProtoPalAttrs &attrs : from) {
|
||||||
to.assign(std::move(protoPal));
|
to.assign(attrs.protoPalIndex);
|
||||||
}
|
}
|
||||||
from.clear();
|
from.clear();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
options.verbosePrint(Options::VERB_DEBUG, "%zu palettes after decanting on palettes\n",
|
||||||
|
assignments.size());
|
||||||
|
|
||||||
// Decant on "components" (= proto-pals sharing colors)
|
// Decant on "components" (= proto-pals sharing colors)
|
||||||
decantOn([&protoPalettes](AssignedProtos &to, AssignedProtos &from) {
|
decantOn([&protoPalettes](AssignedProtos &to, AssignedProtos &from) {
|
||||||
@@ -301,7 +307,7 @@ static void decant(std::vector<AssignedProtos> &assignments,
|
|||||||
members.clear();
|
members.clear();
|
||||||
assert(members.empty()); // Compiler optimization hint
|
assert(members.empty()); // Compiler optimization hint
|
||||||
do {
|
do {
|
||||||
ProtoPalette const &protoPal = protoPalettes[attrs->palIndex];
|
ProtoPalette const &protoPal = protoPalettes[attrs->protoPalIndex];
|
||||||
// If this is the first proto-pal, or if at least one color matches, add it
|
// If this is the first proto-pal, or if at least one color matches, add it
|
||||||
if (members.empty()
|
if (members.empty()
|
||||||
|| std::find_first_of(colors.begin(), colors.end(), protoPal.begin(),
|
|| std::find_first_of(colors.begin(), colors.end(), protoPal.begin(),
|
||||||
@@ -328,16 +334,20 @@ static void decant(std::vector<AssignedProtos> &assignments,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
options.verbosePrint(Options::VERB_DEBUG, "%zu palettes after decanting on \"components\"\n",
|
||||||
|
assignments.size());
|
||||||
|
|
||||||
// Decant on individual proto-palettes
|
// Decant on individual proto-palettes
|
||||||
decantOn([&protoPalettes](AssignedProtos &to, AssignedProtos &from) {
|
decantOn([&protoPalettes](AssignedProtos &to, AssignedProtos &from) {
|
||||||
for (auto iter = from.begin(); iter != from.end(); ++iter) {
|
for (auto iter = from.begin(); iter != from.end(); ++iter) {
|
||||||
if (to.canFit(protoPalettes[iter->palIndex])) {
|
if (to.canFit(protoPalettes[iter->protoPalIndex])) {
|
||||||
to.assign(std::move(*iter));
|
to.assign(std::move(*iter));
|
||||||
from.remove(iter);
|
from.remove(iter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
options.verbosePrint(Options::VERB_DEBUG, "%zu palettes after decanting on proto-palettes\n",
|
||||||
|
assignments.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tuple<DefaultInitVec<size_t>, size_t>
|
std::tuple<DefaultInitVec<size_t>, size_t>
|
||||||
@@ -376,8 +386,9 @@ std::tuple<DefaultInitVec<size_t>, size_t>
|
|||||||
|
|
||||||
for (; !queue.empty(); queue.pop()) {
|
for (; !queue.empty(); queue.pop()) {
|
||||||
ProtoPalAttrs const &attrs = queue.front(); // Valid until the `queue.pop()`
|
ProtoPalAttrs const &attrs = queue.front(); // Valid until the `queue.pop()`
|
||||||
|
options.verbosePrint(Options::VERB_DEBUG, "Handling proto-pal %zu\n", attrs.protoPalIndex);
|
||||||
|
|
||||||
ProtoPalette const &protoPal = protoPalettes[attrs.palIndex];
|
ProtoPalette const &protoPal = protoPalettes[attrs.protoPalIndex];
|
||||||
size_t bestPalIndex = assignments.size();
|
size_t bestPalIndex = assignments.size();
|
||||||
// We're looking for a palette where the proto-palette's relative size is less than
|
// We're looking for a palette where the proto-palette's relative size is less than
|
||||||
// its actual size; so only overwrite the "not found" index on meeting that criterion
|
// its actual size; so only overwrite the "not found" index on meeting that criterion
|
||||||
@@ -419,15 +430,15 @@ std::tuple<DefaultInitVec<size_t>, size_t>
|
|||||||
std::minmax_element(bestPal.begin(), bestPal.end(),
|
std::minmax_element(bestPal.begin(), bestPal.end(),
|
||||||
[&efficiency, &protoPalettes](ProtoPalAttrs const &lhs,
|
[&efficiency, &protoPalettes](ProtoPalAttrs const &lhs,
|
||||||
ProtoPalAttrs const &rhs) {
|
ProtoPalAttrs const &rhs) {
|
||||||
return efficiency(protoPalettes[lhs.palIndex])
|
return efficiency(protoPalettes[lhs.protoPalIndex])
|
||||||
< efficiency(protoPalettes[rhs.palIndex]);
|
< efficiency(protoPalettes[rhs.protoPalIndex]);
|
||||||
});
|
});
|
||||||
|
|
||||||
// All efficiencies are identical iff min equals max
|
// All efficiencies are identical iff min equals max
|
||||||
// TODO: maybe not ideal to re-compute these two?
|
// TODO: maybe not ideal to re-compute these two?
|
||||||
// TODO: yikes for float comparison! I *think* this threshold is OK?
|
// TODO: yikes for float comparison! I *think* this threshold is OK?
|
||||||
if (efficiency(protoPalettes[maxEfficiencyIter->palIndex])
|
if (efficiency(protoPalettes[maxEfficiencyIter->protoPalIndex])
|
||||||
- efficiency(protoPalettes[minEfficiencyIter->palIndex])
|
- efficiency(protoPalettes[minEfficiencyIter->protoPalIndex])
|
||||||
< .001) {
|
< .001) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -452,21 +463,37 @@ std::tuple<DefaultInitVec<size_t>, size_t>
|
|||||||
// Place back any proto-palettes now in the queue via first-fit
|
// Place back any proto-palettes now in the queue via first-fit
|
||||||
while (!queue.empty()) {
|
while (!queue.empty()) {
|
||||||
ProtoPalAttrs const &attrs = queue.front();
|
ProtoPalAttrs const &attrs = queue.front();
|
||||||
ProtoPalette const &protoPal = protoPalettes[attrs.palIndex];
|
ProtoPalette const &protoPal = protoPalettes[attrs.protoPalIndex];
|
||||||
auto iter =
|
auto iter =
|
||||||
std::find_if(assignments.begin(), assignments.end(),
|
std::find_if(assignments.begin(), assignments.end(),
|
||||||
[&protoPal](AssignedProtos const &pal) { return pal.canFit(protoPal); });
|
[&protoPal](AssignedProtos const &pal) { return pal.canFit(protoPal); });
|
||||||
if (iter == assignments.end()) { // No such page, create a new one
|
if (iter == assignments.end()) { // No such page, create a new one
|
||||||
options.verbosePrint(Options::VERB_DEBUG, "Adding new palette for overflow\n");
|
options.verbosePrint(Options::VERB_DEBUG,
|
||||||
|
"Adding new palette (%zu) for overflowing proto-pal %zu\n",
|
||||||
|
assignments.size(), attrs.protoPalIndex);
|
||||||
assignments.emplace_back(protoPalettes, std::move(attrs));
|
assignments.emplace_back(protoPalettes, std::move(attrs));
|
||||||
} else {
|
} else {
|
||||||
options.verbosePrint(Options::VERB_DEBUG, "Assigning overflow to palette %zu\n",
|
options.verbosePrint(Options::VERB_DEBUG,
|
||||||
iter - assignments.begin());
|
"Assigning overflowing proto-pal %zu to palette %zu\n",
|
||||||
|
attrs.protoPalIndex, iter - assignments.begin());
|
||||||
iter->assign(std::move(attrs));
|
iter->assign(std::move(attrs));
|
||||||
}
|
}
|
||||||
queue.pop();
|
queue.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.verbosity >= Options::VERB_INTERM) {
|
||||||
|
for (auto &&assignment : assignments) {
|
||||||
|
fprintf(stderr, "{ ");
|
||||||
|
for (auto &&attrs : assignment) {
|
||||||
|
fprintf(stderr, "[%zu] ", attrs.protoPalIndex);
|
||||||
|
for (auto &&colorIndex : protoPalettes[attrs.protoPalIndex]) {
|
||||||
|
fprintf(stderr, "%04" PRIx16 ", ", colorIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf(stderr, "} (volume = %zu)\n", assignment.volume());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// "Decant" the result
|
// "Decant" the result
|
||||||
decant(assignments, protoPalettes);
|
decant(assignments, protoPalettes);
|
||||||
// Note that the result does not contain any empty palettes
|
// Note that the result does not contain any empty palettes
|
||||||
@@ -475,7 +502,8 @@ std::tuple<DefaultInitVec<size_t>, size_t>
|
|||||||
for (auto &&assignment : assignments) {
|
for (auto &&assignment : assignments) {
|
||||||
fprintf(stderr, "{ ");
|
fprintf(stderr, "{ ");
|
||||||
for (auto &&attrs : assignment) {
|
for (auto &&attrs : assignment) {
|
||||||
for (auto &&colorIndex : protoPalettes[attrs.palIndex]) {
|
fprintf(stderr, "[%zu] ", attrs.protoPalIndex);
|
||||||
|
for (auto &&colorIndex : protoPalettes[attrs.protoPalIndex]) {
|
||||||
fprintf(stderr, "%04" PRIx16 ", ", colorIndex);
|
fprintf(stderr, "%04" PRIx16 ", ", colorIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -486,7 +514,7 @@ std::tuple<DefaultInitVec<size_t>, size_t>
|
|||||||
DefaultInitVec<size_t> mappings(protoPalettes.size());
|
DefaultInitVec<size_t> mappings(protoPalettes.size());
|
||||||
for (size_t i = 0; i < assignments.size(); ++i) {
|
for (size_t i = 0; i < assignments.size(); ++i) {
|
||||||
for (ProtoPalAttrs const &attrs : assignments[i]) {
|
for (ProtoPalAttrs const &attrs : assignments[i]) {
|
||||||
mappings[attrs.palIndex] = i;
|
mappings[attrs.protoPalIndex] = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {mappings, assignments.size()};
|
return {mappings, assignments.size()};
|
||||||
|
|||||||
Reference in New Issue
Block a user