Merge pull request #145 from WaywardHeart/deletedBitmaps

Guard against deleted bitmaps
This commit is contained in:
Splendide Imaginarius 2024-03-22 06:47:14 +00:00 committed by GitHub
commit 1b3d1d96c4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 160 additions and 32 deletions

View file

@ -2592,24 +2592,6 @@ int Bitmap::maxSize(){
return glState.caps.maxTexSize; return glState.caps.maxTexSize;
} }
// This might look ridiculous, but apparently, it is possible
// to encounter seemingly empty bitmaps during Graphics::update,
// or specifically, during a Sprite's prepare function.
// I have no idea why it happens, but it seems like just skipping
// them makes it okay, so... that's what this function is for, at
// least unless the actual source of the problem gets found, at
// which point I'd get rid of it.
// I get it to happen by trying to beat the first rival fight in
// Pokemon Flux, on macOS. I don't think I've seen anyone bring up
// something like this happening anywhere else, so... I dunno.
// If a game suddenly explodes during Graphics.update, maybe try
// breakpointing this?
bool Bitmap::invalid() const {
return p == 0;
}
void Bitmap::assumeRubyGC() void Bitmap::assumeRubyGC()
{ {
p->assumingRubyGC = true; p->assumingRubyGC = true;

View file

@ -167,8 +167,6 @@ public:
static int maxSize(); static int maxSize();
bool invalid() const;
void assumeRubyGC(); void assumeRubyGC();
private: private:

View file

@ -46,6 +46,8 @@ struct PlanePrivate
{ {
Bitmap *bitmap; Bitmap *bitmap;
sigslot::connection bitmapDispCon;
NormValue opacity; NormValue opacity;
BlendType blendType; BlendType blendType;
Color *color; Color *color;
@ -83,6 +85,14 @@ struct PlanePrivate
~PlanePrivate() ~PlanePrivate()
{ {
prepareCon.disconnect(); prepareCon.disconnect();
bitmapDisposal();
}
void bitmapDisposal()
{
bitmap = 0;
bitmapDispCon.disconnect();
} }
void updateQuadSource() void updateQuadSource()
@ -138,6 +148,9 @@ struct PlanePrivate
void prepare() void prepare()
{ {
if (nullOrDisposed(bitmap))
return;
if (quadSourceDirty) if (quadSourceDirty)
{ {
updateQuadSource(); updateQuadSource();
@ -176,8 +189,15 @@ void Plane::setBitmap(Bitmap *value)
p->bitmap = value; p->bitmap = value;
if (!value) p->bitmapDispCon.disconnect();
if (nullOrDisposed(value))
{
p->bitmap = 0;
return; return;
}
p->bitmapDispCon = value->wasDisposed.connect(&PlanePrivate::bitmapDisposal, p);
value->ensureNonMega(); value->ensureNonMega();
} }

View file

@ -48,6 +48,8 @@ struct SpritePrivate
{ {
Bitmap *bitmap; Bitmap *bitmap;
sigslot::connection bitmapDispCon;
Quad quad; Quad quad;
Transform trans; Transform trans;
@ -137,6 +139,14 @@ struct SpritePrivate
{ {
srcRectCon.disconnect(); srcRectCon.disconnect();
prepareCon.disconnect(); prepareCon.disconnect();
bitmapDisposal();
}
void bitmapDisposal()
{
bitmap = 0;
bitmapDispCon.disconnect();
} }
void recomputeBushDepth() void recomputeBushDepth()
@ -189,9 +199,6 @@ struct SpritePrivate
if (nullOrDisposed(bitmap)) if (nullOrDisposed(bitmap))
return; return;
if (bitmap->invalid())
return;
if (!opacity) if (!opacity)
return; return;
@ -372,8 +379,15 @@ void Sprite::setBitmap(Bitmap *bitmap)
p->bitmap = bitmap; p->bitmap = bitmap;
p->bitmapDispCon.disconnect();
if (nullOrDisposed(bitmap)) if (nullOrDisposed(bitmap))
{
p->bitmap = 0;
return; return;
}
p->bitmapDispCon = bitmap->wasDisposed.connect(&SpritePrivate::bitmapDisposal, p);
bitmap->ensureNonMega(); bitmap->ensureNonMega();

View file

@ -331,6 +331,8 @@ struct TilemapPrivate
/* Dispose watches */ /* Dispose watches */
sigslot::connection autotilesDispCon[autotileCount]; sigslot::connection autotilesDispCon[autotileCount];
sigslot::connection tilesetDispCon;
/* Draw prepare call */ /* Draw prepare call */
sigslot::connection prepareCon; sigslot::connection prepareCon;
@ -403,6 +405,7 @@ struct TilemapPrivate
/* Disconnect signal handlers */ /* Disconnect signal handlers */
tilesetCon.disconnect(); tilesetCon.disconnect();
tilesetDispCon.disconnect();
for (int i = 0; i < autotileCount; ++i) for (int i = 0; i < autotileCount; ++i)
{ {
autotilesCon[i].disconnect(); autotilesCon[i].disconnect();
@ -490,6 +493,20 @@ struct TilemapPrivate
atlasDirty = true; atlasDirty = true;
} }
void atlasContentsDisposal(int i)
{
// Guard against deleted bitmaps
autotiles[i] = 0;
invalidateAtlasContents();
}
void tilesetDisposal()
{
tileset = 0;
tilesetDispCon.disconnect();
}
void invalidateBuffers() void invalidateBuffers()
{ {
buffersDirty = true; buffersDirty = true;
@ -1189,12 +1206,18 @@ void Tilemap::Autotiles::set(int i, Bitmap *bitmap)
p->invalidateAtlasContents(); p->invalidateAtlasContents();
p->autotilesCon[i].disconnect(); p->autotilesCon[i].disconnect();
p->autotilesDispCon[i].disconnect();
if (nullOrDisposed(bitmap))
{
p->autotiles[i] = 0;
return;
}
p->autotilesCon[i] = bitmap->modified.connect p->autotilesCon[i] = bitmap->modified.connect
(&TilemapPrivate::invalidateAtlasContents, p); (&TilemapPrivate::invalidateAtlasContents, p);
p->autotilesDispCon[i].disconnect(); p->autotilesDispCon[i] = bitmap->wasDisposed.connect( [i, this] { p->atlasContentsDisposal(i); } );
p->autotilesDispCon[i] = bitmap->wasDisposed.connect
(&TilemapPrivate::invalidateAtlasContents, p);
p->updateAutotileInfo(); p->updateAutotileInfo();
} }
@ -1269,14 +1292,23 @@ void Tilemap::setTileset(Bitmap *value)
p->tileset = value; p->tileset = value;
if (!value) p->tilesetDispCon.disconnect();
p->tilesetCon.disconnect();
if (nullOrDisposed(value))
{
p->tileset = 0;
return; return;
}
p->invalidateAtlasSize(); p->invalidateAtlasSize();
p->tilesetCon.disconnect();
p->tilesetCon = value->modified.connect p->tilesetCon = value->modified.connect
(&TilemapPrivate::invalidateAtlasSize, p); (&TilemapPrivate::invalidateAtlasSize, p);
p->tilesetDispCon = value->wasDisposed.connect
(&TilemapPrivate::tilesetDisposal, p);
p->updateAtlasInfo(); p->updateAtlasInfo();
} }

View file

@ -182,6 +182,14 @@ struct TilemapVXPrivate : public ViewportElement, TileAtlasVX::Reader
atlasDirty = true; atlasDirty = true;
} }
void atlasDisposal(int i)
{
// Guard against deleted bitmaps
bitmaps[i] = 0;
invalidateAtlas();
}
void invalidateBuffers() void invalidateBuffers()
{ {
buffersDirty = true; buffersDirty = true;
@ -415,12 +423,18 @@ void TilemapVX::BitmapArray::set(int i, Bitmap *bitmap)
p->atlasDirty = true; p->atlasDirty = true;
p->bmChangedCons[i].disconnect(); p->bmChangedCons[i].disconnect();
p->bmDisposedCons[i].disconnect();
if (nullOrDisposed(bitmap))
{
p->bitmaps[i] = 0;
return;
}
p->bmChangedCons[i] = bitmap->modified.connect p->bmChangedCons[i] = bitmap->modified.connect
(&TilemapVXPrivate::invalidateAtlas, p); (&TilemapVXPrivate::invalidateAtlas, p);
p->bmDisposedCons[i].disconnect(); p->bmDisposedCons[i] = bitmap->wasDisposed.connect( [i, this] { p->atlasDisposal(i); } );
p->bmDisposedCons[i] = bitmap->wasDisposed.connect
(&TilemapVXPrivate::invalidateAtlas, p);
} }
Bitmap *TilemapVX::BitmapArray::get(int i) const Bitmap *TilemapVX::BitmapArray::get(int i) const

View file

@ -172,6 +172,9 @@ struct WindowPrivate
Bitmap *contents; Bitmap *contents;
sigslot::connection windowskinDispCon;
sigslot::connection contentsDispCon;
bool bgStretch; bool bgStretch;
Rect *cursorRect; Rect *cursorRect;
bool active; bool active;
@ -282,6 +285,21 @@ struct WindowPrivate
shState->texPool().release(baseTex); shState->texPool().release(baseTex);
cursorRectCon.disconnect(); cursorRectCon.disconnect();
prepareCon.disconnect(); prepareCon.disconnect();
windowskinDisposal();
contentsDisposal();
}
void windowskinDisposal()
{
windowskin = 0;
windowskinDispCon.disconnect();
}
void contentsDisposal()
{
contents = 0;
contentsDispCon.disconnect();
} }
void markControlVertDirty() void markControlVertDirty()
@ -720,10 +738,17 @@ void Window::setWindowskin(Bitmap *value)
p->windowskin = value; p->windowskin = value;
p->windowskinDispCon.disconnect();
if (nullOrDisposed(value)) if (nullOrDisposed(value))
{
p->windowskin = 0;
return; return;
}
value->ensureNonMega(); value->ensureNonMega();
p->windowskinDispCon = value->wasDisposed.connect(&WindowPrivate::windowskinDisposal, p);
} }
void Window::setContents(Bitmap *value) void Window::setContents(Bitmap *value)
@ -736,10 +761,18 @@ void Window::setContents(Bitmap *value)
p->contents = value; p->contents = value;
p->controlsVertDirty = true; p->controlsVertDirty = true;
p->contentsDispCon.disconnect();
if (nullOrDisposed(value)) if (nullOrDisposed(value))
{
p->contents = 0;
return; return;
}
p->contentsDispCon = value->wasDisposed.connect(&WindowPrivate::contentsDisposal, p);
value->ensureNonMega(); value->ensureNonMega();
p->contentsQuad.setTexPosRect(value->rect(), value->rect()); p->contentsQuad.setTexPosRect(value->rect(), value->rect());
} }

View file

@ -154,6 +154,9 @@ struct WindowVXPrivate
Bitmap *contents; Bitmap *contents;
sigslot::connection windowskinDispCon;
sigslot::connection contentsDispCon;
Rect *cursorRect; Rect *cursorRect;
bool active; bool active;
bool arrowsVisible; bool arrowsVisible;
@ -278,6 +281,21 @@ struct WindowVXPrivate
cursorRectCon.disconnect(); cursorRectCon.disconnect();
toneCon.disconnect(); toneCon.disconnect();
prepareCon.disconnect(); prepareCon.disconnect();
windowskinDisposal();
contentsDisposal();
}
void windowskinDisposal()
{
windowskin = 0;
windowskinDispCon.disconnect();
}
void contentsDisposal()
{
contents = 0;
contentsDispCon.disconnect();
} }
void invalidateCursorVert() void invalidateCursorVert()
@ -908,6 +926,16 @@ void WindowVX::setWindowskin(Bitmap *value)
p->windowskin = value; p->windowskin = value;
p->base.texDirty = true; p->base.texDirty = true;
p->windowskinDispCon.disconnect();
if (nullOrDisposed(value))
{
p->windowskin = 0;
return;
}
p->windowskinDispCon = value->wasDisposed.connect(&WindowVXPrivate::windowskinDisposal, p);
} }
void WindowVX::setContents(Bitmap *value) void WindowVX::setContents(Bitmap *value)
@ -919,8 +947,15 @@ void WindowVX::setContents(Bitmap *value)
p->contents = value; p->contents = value;
p->contentsDispCon.disconnect();
if (nullOrDisposed(value)) if (nullOrDisposed(value))
{
p->contents = 0;
return; return;
}
p->contentsDispCon = value->wasDisposed.connect(&WindowVXPrivate::contentsDisposal, p);
FloatRect rect = p->contents->rect(); FloatRect rect = p->contents->rect();
p->contentsQuad.setTexPosRect(rect, rect); p->contentsQuad.setTexPosRect(rect, rect);