[nostalgia/core] Get TileSheetSet working with 8bpp setting

This commit is contained in:
Gary Talent 2023-12-23 01:23:31 -06:00
parent fcf6f00797
commit 46d1313797
3 changed files with 57 additions and 46 deletions

View File

@ -23,6 +23,7 @@ struct Sprite {
unsigned spriteShape = 0; unsigned spriteShape = 0;
unsigned spriteSize = 0; unsigned spriteSize = 0;
unsigned flipX = 0; unsigned flipX = 0;
unsigned bpp = 0;
/** /**
* Valid priorities: 0-3 * Valid priorities: 0-3
*/ */
@ -38,6 +39,7 @@ oxModelBegin(Sprite)
oxModelField(spriteShape) oxModelField(spriteShape)
oxModelField(spriteSize) oxModelField(spriteSize)
oxModelField(flipX) oxModelField(flipX)
oxModelField(bpp)
oxModelField(priority) oxModelField(priority)
oxModelEnd() oxModelEnd()

View File

@ -60,6 +60,25 @@ constexpr ox::Error model(auto *io, ox::CommonPtrWith<GbaPaletteTarget> auto *t)
} }
} }
[[nodiscard]]
static bool loadPixel(TileSheetSetEntry const&setEntry, size_t &sectionIdx, int tileIdx) noexcept {
if (setEntry.sections.size() <= sectionIdx) {
return false;
}
auto &section = setEntry.sections[sectionIdx];
if (tileIdx < section.begin) {
return false;
}
if (tileIdx > section.end()) {
if (sectionIdx >= setEntry.sections.size()) {
return false;
}
++sectionIdx;
return tileIdx > section.begin && tileIdx <= section.end();
}
return true;
}
constexpr ox::Error model(auto *io, ox::CommonPtrWith<GbaTileMapTarget> auto *t) noexcept { constexpr ox::Error model(auto *io, ox::CommonPtrWith<GbaTileMapTarget> auto *t) noexcept {
oxReturnError(io->template setTypeInfo<CompactTileSheet>()); oxReturnError(io->template setTypeInfo<CompactTileSheet>());
oxReturnError(io->field("bpp", &t->bpp)); oxReturnError(io->field("bpp", &t->bpp));
@ -71,43 +90,27 @@ constexpr ox::Error model(auto *io, ox::CommonPtrWith<GbaTileMapTarget> auto *t)
return OxError(1, "Cannot load an 8 BPP tilesheet into a 4 BPP CBB"); return OxError(1, "Cannot load an 8 BPP tilesheet into a 4 BPP CBB");
} }
ox::Error out{}; ox::Error out{};
uint16_t intermediate = 0;
if (t->setEntry) { if (t->setEntry) {
// The following code is atrocious, but it works. // The following code is atrocious, but it works.
// It might be possible to clean it up a little, but it probably // It might be possible to clean it up a little, but it probably
// cannot be seriously optimized without preloading TileSheets. // cannot be seriously optimized without preloading TileSheets.
// Consider building an array of bitmaps (Array<uint64_t, 4>) size_t sectionIdx = 0;
// so this can be done with a single bitmap look up.
if (t->targetBpp == t->bpp) { if (t->targetBpp == t->bpp) {
TileSheetSetEntrySection const*section = &t->setEntry->sections[0]; TileSheetSetEntrySection const*section = &t->setEntry->sections[0];
size_t sectionIdx = 1;
size_t writeIt = 0; size_t writeIt = 0;
uint16_t intermediate = 0;
size_t const fourBpp = t->bpp == 4; size_t const fourBpp = t->bpp == 4;
const auto handleTileMap = [t, &intermediate, &section, &sectionIdx, &writeIt, fourBpp] const auto handleTileMap = [t, &intermediate, &section, &sectionIdx, &writeIt, fourBpp]
(std::size_t i, uint8_t const *tile) { (std::size_t i, uint8_t const*tile) {
if (!section) {
return ox::Error{};
}
auto const tileIdx = static_cast<int>((i * (2 * fourBpp)) / PixelsPerTile); auto const tileIdx = static_cast<int>((i * (2 * fourBpp)) / PixelsPerTile);
if (tileIdx < section->begin) { if (!loadPixel(*t->setEntry, sectionIdx, tileIdx)) {
return ox::Error{}; return ox::Error{};
} }
if (tileIdx > section->end()) { if (writeIt & 1) { // i is odd
if (sectionIdx >= t->setEntry->sections.size()) {
section = nullptr;
return ox::Error{};
}
section = &t->setEntry->sections[sectionIdx];
++sectionIdx;
if (tileIdx < section->begin || tileIdx > section->end()) {
return ox::Error{};
}
}
if (writeIt & 1) { // writeIt is odd
intermediate |= static_cast<uint16_t>(*tile) << 8; intermediate |= static_cast<uint16_t>(*tile) << 8;
t->tileMap[writeIt / 2] = intermediate; t->tileMap[writeIt / 2] = intermediate;
++t->tileWriteIdx; ++t->tileWriteIdx;
} else { // writeIt is even } else { // i is even
intermediate = *tile & 0x00ff; intermediate = *tile & 0x00ff;
} }
++writeIt; ++writeIt;
@ -115,31 +118,30 @@ constexpr ox::Error model(auto *io, ox::CommonPtrWith<GbaTileMapTarget> auto *t)
}; };
out = io->template field<uint8_t, decltype(handleTileMap)>("tileMap", handleTileMap); out = io->template field<uint8_t, decltype(handleTileMap)>("tileMap", handleTileMap);
} else if (t->targetBpp > t->bpp) { // 4 -> 8 bits } else if (t->targetBpp > t->bpp) { // 4 -> 8 bits
const auto handleTileMap = [t, &intermediate](std::size_t i, uint8_t const *tile) { const auto handleTileMap = [t, &sectionIdx](std::size_t writeIt, uint8_t const*tile) {
if (i & 1) { // i is odd auto constexpr BytesPerTile4Bpp = 32;
intermediate |= static_cast<uint16_t>(*tile) << 8; auto const tileIdx = static_cast<int>(writeIt / BytesPerTile4Bpp);
// write 4 pixels from intermediate if (!loadPixel(*t->setEntry, sectionIdx, tileIdx)) {
t->tileMap[i - 3] = (intermediate & 0b0000'0000'0000'1111) >> 0; return ox::Error{};
t->tileMap[i - 2] = (intermediate & 0b0000'0000'1111'0000) >> 4;
t->tileMap[i - 1] = (intermediate & 0b0000'1111'0000'0000) >> 8;
t->tileMap[i - 0] = (intermediate & 0b1111'0000'0000'0000) >> 14;
++t->tileWriteIdx;
} else { // i is even
intermediate = *tile & 0x00ff;
} }
uint16_t const px1 = *tile & 0xf;
uint16_t const px2 = *tile >> 4;
t->tileMap[t->tileWriteIdx] = static_cast<uint16_t>(px1 | (px2 << 8));
++t->tileWriteIdx;
return ox::Error{}; return ox::Error{};
}; };
out = io->template field<uint8_t, decltype(handleTileMap)>("tileMap", handleTileMap); out = io->template field<uint8_t, decltype(handleTileMap)>("tileMap", handleTileMap);
} }
} else { } else {
const auto handleTileMap = [t, &intermediate](std::size_t i, const uint8_t *tile) { uint16_t intermediate = 0;
const auto handleTileMap = [t, &intermediate](std::size_t i, const uint8_t*tile) {
if (i & 1) { // i is odd if (i & 1) { // i is odd
intermediate |= static_cast<uint16_t>(*tile) << 8; intermediate |= static_cast<uint16_t>(*tile) << 8;
t->tileMap[i / 2] = intermediate; t->tileMap[i / 2] = intermediate;
} else { // i is even } else { // i is even
intermediate = *tile & 0x00ff; intermediate = *tile & 0x00ff;
} }
return OxError(0); return ox::Error{};
}; };
out = io->template field<uint8_t, decltype(handleTileMap)>("tileMap", handleTileMap); out = io->template field<uint8_t, decltype(handleTileMap)>("tileMap", handleTileMap);
} }
@ -181,7 +183,6 @@ ox::Error loadSpritePalette(
static ox::Error loadTileSheetSet( static ox::Error loadTileSheetSet(
Context &ctx, Context &ctx,
unsigned &bpp,
uint16_t *tileMapTargetMem, uint16_t *tileMapTargetMem,
TileSheetSet const&set) noexcept { TileSheetSet const&set) noexcept {
auto &rom = ctx.rom(); auto &rom = ctx.rom();
@ -189,10 +190,12 @@ static ox::Error loadTileSheetSet(
for (auto const&entry : set.entries) { for (auto const&entry : set.entries) {
oxRequire(tsStat, rom.stat(entry.tilesheet)); oxRequire(tsStat, rom.stat(entry.tilesheet));
oxRequire(ts, rom.directAccess(entry.tilesheet)); oxRequire(ts, rom.directAccess(entry.tilesheet));
unsigned tilesheetBpp{};
GbaTileMapTarget target{ GbaTileMapTarget target{
.bpp = bpp, .bpp = tilesheetBpp,
.defaultPalette = {}, .defaultPalette = {},
.tileMap = tileMapTargetMem + tileWriteIdx, .tileMap = tileMapTargetMem + tileWriteIdx,
.targetBpp = static_cast<unsigned>(set.bpp),
.setEntry = &entry, .setEntry = &entry,
}; };
oxReturnError(ox::readMC(ts, static_cast<std::size_t>(tsStat.size), &target)); oxReturnError(ox::readMC(ts, static_cast<std::size_t>(tsStat.size), &target));
@ -232,9 +235,10 @@ ox::Error loadBgTileSheet(
Context &ctx, Context &ctx,
unsigned cbb, unsigned cbb,
TileSheetSet const&set) noexcept { TileSheetSet const&set) noexcept {
auto &bpp = ctx.cbbData[cbb].bpp; auto const bpp = static_cast<unsigned>(set.bpp);
oxReturnError(loadTileSheetSet(ctx, bpp, MEM_BG_TILES[cbb].data(), set)); oxReturnError(loadTileSheetSet(ctx, MEM_BG_TILES[cbb].data(), set));
// update bpp of all bgs with the updated cbb // update bpp of all bgs with the updated cbb
ctx.cbbData[cbb].bpp = bpp;
teagba::iterateBgCtl([bpp, cbb](volatile BgCtl &bgCtl) { teagba::iterateBgCtl([bpp, cbb](volatile BgCtl &bgCtl) {
if (teagba::bgCbb(bgCtl) == cbb) { if (teagba::bgCbb(bgCtl) == cbb) {
teagba::bgSetBpp(bgCtl, bpp); teagba::bgSetBpp(bgCtl, bpp);
@ -276,8 +280,8 @@ ox::Error loadSpriteTileSheet(
ox::Error loadSpriteTileSheet( ox::Error loadSpriteTileSheet(
Context &ctx, Context &ctx,
TileSheetSet const&set) noexcept { TileSheetSet const&set) noexcept {
unsigned bpp{}; auto const bpp = static_cast<unsigned>(set.bpp);
oxReturnError(loadTileSheetSet(ctx, bpp, MEM_SPRITE_TILES, set)); oxReturnError(loadTileSheetSet(ctx, MEM_SPRITE_TILES, set));
setSpritesBpp(bpp); setSpritesBpp(bpp);
return {}; return {};
} }
@ -340,17 +344,20 @@ void showSprite(Context&, unsigned idx) noexcept {
void setSprite(Context&, uint_t idx, Sprite const&s) noexcept { void setSprite(Context&, uint_t idx, Sprite const&s) noexcept {
//oxAssert(g_spriteUpdates < config::GbaSpriteBufferLen, "Sprite update buffer overflow"); //oxAssert(g_spriteUpdates < config::GbaSpriteBufferLen, "Sprite update buffer overflow");
uint16_t const eightBpp = s.bpp == 8;
teagba::addSpriteUpdate({ teagba::addSpriteUpdate({
.attr0 = static_cast<uint16_t>( .attr0 = static_cast<uint16_t>(
(static_cast<uint16_t>(s.y & ox::onMask<uint8_t>(0b111'1111))) (static_cast<uint16_t>(s.y & ox::onMask<uint8_t>(0b111'1111)))
| (static_cast<uint16_t>(1) << 10) // enable alpha | (static_cast<uint16_t>(1) << 10) // enable alpha
| (static_cast<uint16_t>(eightBpp) << 13)
| (static_cast<uint16_t>(s.spriteShape) << 14)), | (static_cast<uint16_t>(s.spriteShape) << 14)),
.attr1 = static_cast<uint16_t>( .attr1 = static_cast<uint16_t>(
(static_cast<uint16_t>(s.x) & ox::onMask<uint8_t>(8)) (static_cast<uint16_t>(s.x) & ox::onMask<uint8_t>(8))
| (static_cast<uint16_t>(s.flipX) << 12) | (static_cast<uint16_t>(s.flipX) << 12)
| (static_cast<uint16_t>(s.spriteSize) << 14)), | (static_cast<uint16_t>(s.spriteSize) << 14)),
.attr2 = static_cast<uint16_t>( .attr2 = static_cast<uint16_t>(
(static_cast<uint16_t>(s.tileIdx & ox::onMask<uint16_t>(8))) // double tileIdx if 8 bpp
(static_cast<uint16_t>((s.tileIdx * (1 + eightBpp)) & ox::onMask<uint16_t>(8)))
| (static_cast<uint16_t>(s.priority & 0b11) << 10)), | (static_cast<uint16_t>(s.priority & 0b11) << 10)),
.idx = static_cast<uint16_t>(idx), .idx = static_cast<uint16_t>(idx),
}); });

View File

@ -105,16 +105,18 @@ static ox::Error runTileSheetSetTest(turbine::Context &tctx) {
core::setBgTile(*cctx, 0, 11, 9, 2); core::setBgTile(*cctx, 0, 11, 9, 2);
core::setBgTile(*cctx, 0, 13, 9, 4); core::setBgTile(*cctx, 0, 13, 9, 4);
core::setSprite(*cctx, 16, { core::setSprite(*cctx, 16, {
.enabled = true, .enabled = true,
.x = 12 * 8, .x = 12 * 8,
.y = 9 * 8, .y = 9 * 8,
.tileIdx = 3, .tileIdx = 3,
.bpp = static_cast<unsigned>(set.bpp),
}); });
core::setSprite(*cctx, 17, { core::setSprite(*cctx, 17, {
.enabled = true, .enabled = true,
.x = 14 * 8, .x = 14 * 8,
.y = 9 * 8, .y = 9 * 8,
.tileIdx = 5, .tileIdx = 5,
.bpp = static_cast<unsigned>(set.bpp),
}); });
turbine::setKeyEventHandler(tctx, testKeyEventHandler); turbine::setKeyEventHandler(tctx, testKeyEventHandler);
return turbine::run(tctx); return turbine::run(tctx);