Lots of fixes. Maybe.
This commit is contained in:
parent
c80610747a
commit
4fdb757671
4 changed files with 424 additions and 247 deletions
17
agi.c
17
agi.c
|
|
@ -152,7 +152,10 @@ static jlSurfaceT *gBackdrop;
|
||||||
static jlSurfaceT *gStage;
|
static jlSurfaceT *gStage;
|
||||||
static AgiVmT gVm;
|
static AgiVmT gVm;
|
||||||
static LogicCacheSlotT gLogicCache[AGI_MAX_RESOURCES];
|
static LogicCacheSlotT gLogicCache[AGI_MAX_RESOURCES];
|
||||||
static ViewCacheSlotT gViewCache[AGI_MAX_RESOURCES];
|
// View cache: 256 slots of ~5 KB each (~1.3 MB). Far too large for the
|
||||||
|
// IIgs bank-0 BSS, so it is jlAlloc'd at startup (Memory Manager handle
|
||||||
|
// on the IIgs, malloc elsewhere) rather than declared as a static array.
|
||||||
|
static ViewCacheSlotT *gViewCache;
|
||||||
static bool gPicReady;
|
static bool gPicReady;
|
||||||
static uint8_t gOverrideRoom;
|
static uint8_t gOverrideRoom;
|
||||||
static bool gOverrideRoomActive;
|
static bool gOverrideRoomActive;
|
||||||
|
|
@ -1729,6 +1732,16 @@ int main(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The view cache is too large for bank-0 BSS on the IIgs; pull it
|
||||||
|
// from the platform's large-allocator. jlAlloc does not zero its
|
||||||
|
// result, so clear it so every slot starts with loaded == false.
|
||||||
|
gViewCache = (ViewCacheSlotT *)jlAlloc(sizeof(ViewCacheSlotT) * AGI_MAX_RESOURCES);
|
||||||
|
if (gViewCache == NULL) {
|
||||||
|
jlShutdown();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
memset(gViewCache, 0, sizeof(ViewCacheSlotT) * AGI_MAX_RESOURCES);
|
||||||
|
|
||||||
gameDir = resolveGameDir(argc, argv);
|
gameDir = resolveGameDir(argc, argv);
|
||||||
startingRoom = resolveStartingRoom(argc, argv);
|
startingRoom = resolveStartingRoom(argc, argv);
|
||||||
gOverrideRoom = startingRoom;
|
gOverrideRoom = startingRoom;
|
||||||
|
|
@ -1858,6 +1871,8 @@ int main(int argc, char **argv) {
|
||||||
jlAudioShutdown();
|
jlAudioShutdown();
|
||||||
releaseLogicCache();
|
releaseLogicCache();
|
||||||
releaseViewCache();
|
releaseViewCache();
|
||||||
|
jlFree(gViewCache);
|
||||||
|
gViewCache = NULL;
|
||||||
if (gBackdrop != NULL) {
|
if (gBackdrop != NULL) {
|
||||||
jlSurfaceDestroy(gBackdrop);
|
jlSurfaceDestroy(gBackdrop);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
9
agiPic.c
9
agiPic.c
|
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
#include "agi.h"
|
#include "agi.h"
|
||||||
|
|
||||||
|
#include "joey/core.h"
|
||||||
#include "joey/draw.h"
|
#include "joey/draw.h"
|
||||||
#include "surfaceInternal.h"
|
#include "surfaceInternal.h"
|
||||||
|
|
||||||
|
|
@ -408,8 +409,8 @@ static void plot(AgiPicT *pic, const PicStateT *state, int16_t x, int16_t y) {
|
||||||
// ----- Public API (alphabetical) -----
|
// ----- Public API (alphabetical) -----
|
||||||
|
|
||||||
bool agiPicAlloc(AgiPicT *pic) {
|
bool agiPicAlloc(AgiPicT *pic) {
|
||||||
pic->visual = (uint8_t *)malloc(AGI_PIC_PIXELS);
|
pic->visual = (uint8_t *)jlAlloc(AGI_PIC_PIXELS);
|
||||||
pic->priority = (uint8_t *)malloc(AGI_PIC_PIXELS);
|
pic->priority = (uint8_t *)jlAlloc(AGI_PIC_PIXELS);
|
||||||
if (pic->visual == NULL || pic->priority == NULL) {
|
if (pic->visual == NULL || pic->priority == NULL) {
|
||||||
agiPicFree(pic);
|
agiPicFree(pic);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -651,11 +652,11 @@ void agiPicAddView(AgiPicT *pic, const AgiViewT *view,
|
||||||
|
|
||||||
void agiPicFree(AgiPicT *pic) {
|
void agiPicFree(AgiPicT *pic) {
|
||||||
if (pic->visual != NULL) {
|
if (pic->visual != NULL) {
|
||||||
free(pic->visual);
|
jlFree(pic->visual);
|
||||||
pic->visual = NULL;
|
pic->visual = NULL;
|
||||||
}
|
}
|
||||||
if (pic->priority != NULL) {
|
if (pic->priority != NULL) {
|
||||||
free(pic->priority);
|
jlFree(pic->priority);
|
||||||
pic->priority = NULL;
|
pic->priority = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
256
agiView.c
256
agiView.c
|
|
@ -43,14 +43,157 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// ----- File-local types -----
|
||||||
|
|
||||||
|
// Per-draw invariants for one cel blit, grouped so they live in memory
|
||||||
|
// rather than competing for the w65816's tiny register file. Passing a
|
||||||
|
// pointer to this keeps the hot blit helpers under the backend's
|
||||||
|
// register-allocation ceiling.
|
||||||
|
typedef struct {
|
||||||
|
const uint8_t *picPriority;
|
||||||
|
int16_t x;
|
||||||
|
int16_t width;
|
||||||
|
uint8_t actorPri;
|
||||||
|
uint8_t transparent;
|
||||||
|
bool mirrored;
|
||||||
|
} AgiCelBlitT;
|
||||||
|
|
||||||
|
|
||||||
// ----- Prototypes -----
|
// ----- Prototypes -----
|
||||||
|
|
||||||
|
static void blitPlanar(const AgiCelBlitT *blit, const uint8_t *rle, int16_t height, int16_t topY, jlSurfaceT *stage, int16_t destY);
|
||||||
|
static const uint8_t *blitRowChunky(const AgiCelBlitT *blit, const uint8_t *rle, const uint8_t *priRow, uint8_t *stageRow);
|
||||||
|
static void markCelDirty(jlSurfaceT *stage, int16_t x, int16_t width, int16_t destY, int16_t topY, int16_t height);
|
||||||
static bool parseLoop(AgiLoopInfoT *loop, const uint8_t *loopStart, uint16_t maxBytes);
|
static bool parseLoop(AgiLoopInfoT *loop, const uint8_t *loopStart, uint16_t maxBytes);
|
||||||
static const AgiCelInfoT *resolveMirror(const AgiViewT *view, uint8_t loopIdx, uint8_t celIdx, bool *outMirrored);
|
static const AgiCelInfoT *resolveMirror(const AgiViewT *view, uint8_t loopIdx, uint8_t celIdx, bool *outMirrored);
|
||||||
|
|
||||||
|
|
||||||
// ----- Internal helpers (alphabetical) -----
|
// ----- Internal helpers (alphabetical) -----
|
||||||
|
|
||||||
|
// Planar fallback row walker (stage->pixels == NULL). Routes every
|
||||||
|
// opaque, visible pixel through jlDrawPixel so the port's c2p /
|
||||||
|
// plane-sync path stays correct. Split out of agiViewDraw so its
|
||||||
|
// locals don't pile onto the chunky path's register budget.
|
||||||
|
static void blitPlanar(const AgiCelBlitT *blit, const uint8_t *rle, int16_t height, int16_t topY, jlSurfaceT *stage, int16_t destY) {
|
||||||
|
int16_t width;
|
||||||
|
int16_t cy;
|
||||||
|
int16_t cx;
|
||||||
|
int16_t drawCx;
|
||||||
|
int16_t agiX;
|
||||||
|
int16_t agiY;
|
||||||
|
int16_t stageX;
|
||||||
|
int16_t stageY;
|
||||||
|
uint8_t rleByte;
|
||||||
|
uint8_t color;
|
||||||
|
uint8_t runLen;
|
||||||
|
uint8_t r;
|
||||||
|
uint8_t picPri;
|
||||||
|
|
||||||
|
width = blit->width;
|
||||||
|
for (cy = 0; cy < height; cy++) {
|
||||||
|
cx = 0;
|
||||||
|
for (;;) {
|
||||||
|
rleByte = *rle++;
|
||||||
|
if (rleByte == 0u) { break; }
|
||||||
|
color = (uint8_t)(rleByte >> 4);
|
||||||
|
runLen = (uint8_t)(rleByte & 0x0Fu);
|
||||||
|
for (r = 0u; r < runLen; r++) {
|
||||||
|
if (cx >= width) { break; }
|
||||||
|
if (color != blit->transparent) {
|
||||||
|
drawCx = blit->mirrored ? (int16_t)(width - 1 - cx) : cx;
|
||||||
|
agiX = (int16_t)(blit->x + drawCx);
|
||||||
|
agiY = (int16_t)(topY + cy);
|
||||||
|
if (agiX >= 0 && agiX < (int16_t)AGI_PIC_WIDTH && agiY >= 0 && agiY < (int16_t)AGI_PIC_HEIGHT) {
|
||||||
|
picPri = blit->picPriority[agiY * (int16_t)AGI_PIC_WIDTH + agiX];
|
||||||
|
if (picPri <= blit->actorPri) {
|
||||||
|
stageX = (int16_t)(agiX << 1);
|
||||||
|
stageY = (int16_t)(destY + agiY);
|
||||||
|
jlDrawPixel(stage, stageX, stageY, color);
|
||||||
|
jlDrawPixel(stage, (int16_t)(stageX + 1), stageY, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Chunky fast-path walker for one source row. Consumes the cel's RLE
|
||||||
|
// stream from `rle`, writing pixel-doubled bytes into `stageRow` where
|
||||||
|
// the picture priority allows it, and returns the advanced RLE pointer
|
||||||
|
// so the caller can hand it to the next row. A NULL priRow / stageRow
|
||||||
|
// means the row is clipped out vertically: the RLE is still walked (so
|
||||||
|
// the pointer lands correctly for later rows) but nothing is drawn.
|
||||||
|
static const uint8_t *blitRowChunky(const AgiCelBlitT *blit, const uint8_t *rle, const uint8_t *priRow, uint8_t *stageRow) {
|
||||||
|
int16_t width;
|
||||||
|
int16_t cx;
|
||||||
|
int16_t drawCx;
|
||||||
|
int16_t agiX;
|
||||||
|
uint8_t rleByte;
|
||||||
|
uint8_t color;
|
||||||
|
uint8_t runLen;
|
||||||
|
uint8_t packed;
|
||||||
|
uint8_t r;
|
||||||
|
|
||||||
|
width = blit->width;
|
||||||
|
cx = 0;
|
||||||
|
for (;;) {
|
||||||
|
rleByte = *rle++;
|
||||||
|
if (rleByte == 0u) { break; }
|
||||||
|
color = (uint8_t)(rleByte >> 4);
|
||||||
|
runLen = (uint8_t)(rleByte & 0x0Fu);
|
||||||
|
// Transparent runs and vertically-clipped rows both just skip
|
||||||
|
// ahead by runLen without touching the stage.
|
||||||
|
if (color == blit->transparent || priRow == NULL) {
|
||||||
|
cx = (int16_t)(cx + runLen);
|
||||||
|
if (cx > width) { cx = width; }
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
packed = (uint8_t)((color << 4) | color);
|
||||||
|
for (r = 0u; r < runLen; r++) {
|
||||||
|
if (cx >= width) { break; }
|
||||||
|
drawCx = blit->mirrored ? (int16_t)(width - 1 - cx) : cx;
|
||||||
|
agiX = (int16_t)(blit->x + drawCx);
|
||||||
|
if (agiX >= 0 && agiX < (int16_t)AGI_PIC_WIDTH) {
|
||||||
|
if (priRow[agiX] <= blit->actorPri) {
|
||||||
|
stageRow[agiX] = packed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rle;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Mark the cel's stage bounding box dirty once after a chunky blit.
|
||||||
|
// Computes the pixel-doubled bbox from the source rect, clips it to the
|
||||||
|
// stage so the surface marker doesn't reject the whole rect for an
|
||||||
|
// off-screen edge, and records it if any of it is on-screen.
|
||||||
|
static void markCelDirty(jlSurfaceT *stage, int16_t x, int16_t width, int16_t destY, int16_t topY, int16_t height) {
|
||||||
|
int16_t minStageX;
|
||||||
|
int16_t maxStageX;
|
||||||
|
int16_t minStageY;
|
||||||
|
int16_t maxStageY;
|
||||||
|
|
||||||
|
minStageX = (int16_t)(x << 1);
|
||||||
|
maxStageX = (int16_t)((x + width) << 1);
|
||||||
|
minStageY = (int16_t)(destY + topY);
|
||||||
|
maxStageY = (int16_t)(destY + topY + height);
|
||||||
|
if (minStageX < 0) { minStageX = 0; }
|
||||||
|
if (minStageY < 0) { minStageY = 0; }
|
||||||
|
if (maxStageX > (int16_t)SURFACE_WIDTH) { maxStageX = (int16_t)SURFACE_WIDTH; }
|
||||||
|
if (maxStageY > (int16_t)SURFACE_HEIGHT) { maxStageY = (int16_t)SURFACE_HEIGHT; }
|
||||||
|
if (maxStageX > minStageX && maxStageY > minStageY) {
|
||||||
|
surfaceMarkDirtyRect(stage, minStageX, minStageY,
|
||||||
|
(int16_t)(maxStageX - minStageX),
|
||||||
|
(int16_t)(maxStageY - minStageY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool parseLoop(AgiLoopInfoT *loop, const uint8_t *loopStart, uint16_t maxBytes) {
|
static bool parseLoop(AgiLoopInfoT *loop, const uint8_t *loopStart, uint16_t maxBytes) {
|
||||||
uint8_t celCount;
|
uint8_t celCount;
|
||||||
uint8_t i;
|
uint8_t i;
|
||||||
|
|
@ -149,37 +292,27 @@ void agiViewDraw(const AgiViewT *view, uint8_t loopIdx, uint8_t celIdx,
|
||||||
const uint8_t *picPriority,
|
const uint8_t *picPriority,
|
||||||
jlSurfaceT *stage, int16_t destY) {
|
jlSurfaceT *stage, int16_t destY) {
|
||||||
const AgiCelInfoT *cel;
|
const AgiCelInfoT *cel;
|
||||||
|
AgiCelBlitT blit;
|
||||||
const uint8_t *rle;
|
const uint8_t *rle;
|
||||||
bool mirrored;
|
bool mirrored;
|
||||||
int16_t width;
|
|
||||||
int16_t height;
|
int16_t height;
|
||||||
uint8_t transparent;
|
|
||||||
int16_t topY;
|
int16_t topY;
|
||||||
int16_t cy;
|
uint8_t *stagePixels;
|
||||||
int16_t cx;
|
|
||||||
int16_t drawCx;
|
|
||||||
int16_t agiX;
|
|
||||||
int16_t agiY;
|
|
||||||
int16_t stageX;
|
|
||||||
int16_t stageY;
|
|
||||||
uint8_t rleByte;
|
|
||||||
uint8_t color;
|
|
||||||
uint8_t packed;
|
|
||||||
uint8_t runLen;
|
|
||||||
uint8_t r;
|
|
||||||
uint8_t picPri;
|
|
||||||
uint8_t *stagePixels;
|
|
||||||
|
|
||||||
cel = resolveMirror(view, loopIdx, celIdx, &mirrored);
|
cel = resolveMirror(view, loopIdx, celIdx, &mirrored);
|
||||||
if (cel == NULL) {
|
if (cel == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
width = (int16_t)cel->width;
|
height = (int16_t)cel->height;
|
||||||
height = (int16_t)cel->height;
|
blit.picPriority = picPriority;
|
||||||
transparent = cel->transparentColor;
|
blit.x = x;
|
||||||
rle = cel->rleData;
|
blit.width = (int16_t)cel->width;
|
||||||
topY = (int16_t)(y - height + 1);
|
blit.actorPri = actorPri;
|
||||||
stagePixels = (stage != NULL) ? stage->pixels : NULL;
|
blit.transparent = cel->transparentColor;
|
||||||
|
blit.mirrored = mirrored;
|
||||||
|
rle = cel->rleData;
|
||||||
|
topY = (int16_t)(y - height + 1);
|
||||||
|
stagePixels = (stage != NULL) ? stage->pixels : NULL;
|
||||||
|
|
||||||
// Per-pixel chunky fast path: pixel-doubled output means each
|
// Per-pixel chunky fast path: pixel-doubled output means each
|
||||||
// source pixel maps to exactly one stage byte (both nibbles =
|
// source pixel maps to exactly one stage byte (both nibbles =
|
||||||
|
|
@ -187,10 +320,8 @@ void agiViewDraw(const AgiViewT *view, uint8_t loopIdx, uint8_t celIdx,
|
||||||
// direct, marking the cel bbox dirty once at the end. ~80x
|
// direct, marking the cel bbox dirty once at the end. ~80x
|
||||||
// faster on DOSBox @ 386SX-equivalent CPU cycles.
|
// faster on DOSBox @ 386SX-equivalent CPU cycles.
|
||||||
if (stagePixels != NULL) {
|
if (stagePixels != NULL) {
|
||||||
int16_t minStageX = (int16_t)(x << 1);
|
int16_t cy;
|
||||||
int16_t maxStageX = (int16_t)((x + width) << 1);
|
int16_t agiY;
|
||||||
int16_t minStageY = (int16_t)(destY + topY);
|
|
||||||
int16_t maxStageY = (int16_t)(destY + topY + height);
|
|
||||||
const uint8_t *priRow;
|
const uint8_t *priRow;
|
||||||
uint8_t *stageRow;
|
uint8_t *stageRow;
|
||||||
|
|
||||||
|
|
@ -208,82 +339,15 @@ void agiViewDraw(const AgiViewT *view, uint8_t loopIdx, uint8_t celIdx,
|
||||||
stageRow = stagePixels +
|
stageRow = stagePixels +
|
||||||
(int32_t)(destY + agiY) * (int32_t)SURFACE_BYTES_PER_ROW;
|
(int32_t)(destY + agiY) * (int32_t)SURFACE_BYTES_PER_ROW;
|
||||||
}
|
}
|
||||||
cx = 0;
|
rle = blitRowChunky(&blit, rle, priRow, stageRow);
|
||||||
for (;;) {
|
|
||||||
rleByte = *rle++;
|
|
||||||
if (rleByte == 0u) { break; }
|
|
||||||
color = (uint8_t)(rleByte >> 4);
|
|
||||||
runLen = (uint8_t)(rleByte & 0x0Fu);
|
|
||||||
if (color == transparent) {
|
|
||||||
cx = (int16_t)(cx + runLen);
|
|
||||||
if (cx > width) { cx = width; }
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (priRow == NULL) {
|
|
||||||
cx = (int16_t)(cx + runLen);
|
|
||||||
if (cx > width) { cx = width; }
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
packed = (uint8_t)((color << 4) | color);
|
|
||||||
for (r = 0u; r < runLen; r++) {
|
|
||||||
if (cx >= width) { break; }
|
|
||||||
drawCx = mirrored ? (int16_t)(width - 1 - cx) : cx;
|
|
||||||
agiX = (int16_t)(x + drawCx);
|
|
||||||
if (agiX >= 0 && agiX < (int16_t)AGI_PIC_WIDTH) {
|
|
||||||
if (priRow[agiX] <= actorPri) {
|
|
||||||
stageRow[agiX] = packed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cx++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Dirty-rect tracking at the cel level only: we know the
|
|
||||||
// bbox a priori (the cel's stage rect), and per-pixel tight
|
|
||||||
// bounds aren't worth the inner-loop cost on a 386. Clip to
|
|
||||||
// the stage so the surface marker doesn't reject the whole
|
|
||||||
// rect for an off-screen edge.
|
|
||||||
if (minStageX < 0) { minStageX = 0; }
|
|
||||||
if (minStageY < 0) { minStageY = 0; }
|
|
||||||
if (maxStageX > (int16_t)SURFACE_WIDTH) { maxStageX = (int16_t)SURFACE_WIDTH; }
|
|
||||||
if (maxStageY > (int16_t)SURFACE_HEIGHT) { maxStageY = (int16_t)SURFACE_HEIGHT; }
|
|
||||||
if (maxStageX > minStageX && maxStageY > minStageY) {
|
|
||||||
surfaceMarkDirtyRect(stage, minStageX, minStageY,
|
|
||||||
(int16_t)(maxStageX - minStageX),
|
|
||||||
(int16_t)(maxStageY - minStageY));
|
|
||||||
}
|
}
|
||||||
|
markCelDirty(stage, x, blit.width, destY, topY, height);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Planar fallback (s->pixels NULL: Amiga Phase 9). Goes through
|
// Planar fallback (s->pixels NULL: Amiga Phase 9). Goes through
|
||||||
// jlDrawPixel so the port's c2p / plane-sync path stays correct.
|
// jlDrawPixel so the port's c2p / plane-sync path stays correct.
|
||||||
for (cy = 0; cy < height; cy++) {
|
blitPlanar(&blit, rle, height, topY, stage, destY);
|
||||||
cx = 0;
|
|
||||||
for (;;) {
|
|
||||||
rleByte = *rle++;
|
|
||||||
if (rleByte == 0u) { break; }
|
|
||||||
color = (uint8_t)(rleByte >> 4);
|
|
||||||
runLen = (uint8_t)(rleByte & 0x0Fu);
|
|
||||||
for (r = 0u; r < runLen; r++) {
|
|
||||||
if (cx >= width) { break; }
|
|
||||||
if (color != transparent) {
|
|
||||||
drawCx = mirrored ? (int16_t)(width - 1 - cx) : cx;
|
|
||||||
agiX = (int16_t)(x + drawCx);
|
|
||||||
agiY = (int16_t)(topY + cy);
|
|
||||||
if (agiX >= 0 && agiX < (int16_t)AGI_PIC_WIDTH && agiY >= 0 && agiY < (int16_t)AGI_PIC_HEIGHT) {
|
|
||||||
picPri = picPriority[agiY * (int16_t)AGI_PIC_WIDTH + agiX];
|
|
||||||
if (picPri <= actorPri) {
|
|
||||||
stageX = (int16_t)(agiX << 1);
|
|
||||||
stageY = (int16_t)(destY + agiY);
|
|
||||||
jlDrawPixel(stage, stageX, stageY, color);
|
|
||||||
jlDrawPixel(stage, (int16_t)(stageX + 1), stageY, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cx++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue