Multiple changes

+ Time is now measured in fractions of a second, not microseconds
+ Viewports in RGSS1 will now only retain weak references to their children
+ Invalid Sprite bitmaps found during Graphics.update will be treated as null
+ Bitmap#mega? and Bitmap#animated? are properly rescued
This commit is contained in:
Struma 2023-05-03 23:00:06 -04:00
parent d7bf67fada
commit 0072c19371
14 changed files with 79 additions and 42 deletions

View file

@ -326,8 +326,7 @@ RB_METHOD(mriP) {
RB_METHOD(mkxpDelta) { RB_METHOD(mkxpDelta) {
RB_UNUSED_PARAM; RB_UNUSED_PARAM;
return rb_float_new(shState->runTime());
return ULL2NUM(shState->runTime());
} }
RB_METHOD(mkxpDataDirectory) { RB_METHOD(mkxpDataDirectory) {

View file

@ -434,7 +434,11 @@ RB_METHOD(bitmapGetMega){
Bitmap *b = getPrivateData<Bitmap>(self); Bitmap *b = getPrivateData<Bitmap>(self);
return rb_bool_new(b->isMega()); VALUE ret;
GFX_GUARD_EXC(ret = rb_bool_new(b->isMega()););
return ret;
} }
RB_METHOD(bitmapGetAnimated){ RB_METHOD(bitmapGetAnimated){
@ -444,7 +448,11 @@ RB_METHOD(bitmapGetAnimated){
Bitmap *b = getPrivateData<Bitmap>(self); Bitmap *b = getPrivateData<Bitmap>(self);
return rb_bool_new(b->isAnimated()); VALUE ret;
GFX_GUARD_EXC(ret = rb_bool_new(b->isAnimated()););
return ret;
} }
RB_METHOD(bitmapGetPlaying){ RB_METHOD(bitmapGetPlaying){

View file

@ -37,6 +37,8 @@ disposableAddChild(VALUE disp, VALUE child)
return; return;
} }
VALUE objID = rb_obj_id(child);
VALUE children = rb_iv_get(disp, "children"); VALUE children = rb_iv_get(disp, "children");
bool exists = false; bool exists = false;
@ -47,11 +49,11 @@ disposableAddChild(VALUE disp, VALUE child)
rb_iv_set(disp, "children", children); rb_iv_set(disp, "children", children);
} }
else { else {
exists = RTEST(rb_funcall(children, rb_intern("include?"), 1, child)); exists = RTEST(rb_funcall(children, rb_intern("include?"), 1, objID));
} }
if (!exists) if (!exists)
rb_ary_push(children, child); rb_ary_push(children, objID);
GFX_UNLOCK; GFX_UNLOCK;
} }
@ -63,11 +65,13 @@ disposableRemoveChild(VALUE disp, VALUE child)
return; return;
} }
VALUE objID = rb_obj_id(child);
VALUE children = rb_iv_get(disp, "children"); VALUE children = rb_iv_get(disp, "children");
if (NIL_P(children)) if (NIL_P(children))
return; return;
VALUE index = rb_funcall(children, rb_intern("index"), 1, child); VALUE index = rb_funcall(children, rb_intern("index"), 1, objID);
if (NIL_P(index)) if (NIL_P(index))
return; return;
@ -83,10 +87,16 @@ disposableDisposeChildren(VALUE disp)
if (NIL_P(children)) if (NIL_P(children))
return; return;
ID dispFun = rb_intern("_mkxp_dispose_alias"); for (long i = 0; i < RARRAY_LEN(children); ++i) {
int state;
for (long i = 0; i < RARRAY_LEN(children); ++i) rb_protect([](VALUE args){
rb_funcall2(rb_ary_entry(children, i), dispFun, 0, 0); VALUE objectspace = rb_const_get(rb_cObject, rb_intern("ObjectSpace"));
VALUE ref = rb_funcall(objectspace, rb_intern("_id2ref"), 1, args);
rb_funcall(ref, rb_intern("_mkxp_dispose_alias"), 0);
return Qnil;
}, rb_ary_entry(children, i), &state);
}
//rb_funcall2(rb_ary_entry(children, i), dispFun, 0, 0);
} }
template<class C> template<class C>

View file

@ -32,7 +32,7 @@
RB_METHOD(graphicsDelta) { RB_METHOD(graphicsDelta) {
RB_UNUSED_PARAM; RB_UNUSED_PARAM;
GFX_LOCK; GFX_LOCK;
VALUE ret = ULL2NUM(shState->graphics().getDelta()); VALUE ret = rb_float_new(shState->graphics().getDelta());
GFX_UNLOCK; GFX_UNLOCK;
return ret; return ret;
} }

View file

@ -34,7 +34,7 @@
RB_METHOD(inputDelta) { RB_METHOD(inputDelta) {
RB_UNUSED_PARAM; RB_UNUSED_PARAM;
return ULL2NUM(shState->input().getDelta()); return rb_float_new(shState->input().getDelta());
} }
RB_METHOD(inputUpdate) { RB_METHOD(inputUpdate) {

View file

@ -166,12 +166,11 @@ struct BitmapPrivate
std::vector<TEXFBO> frames; std::vector<TEXFBO> frames;
float fps; float fps;
int lastFrame; int lastFrame;
unsigned long long startTime; double startTime, playTime;
unsigned long long playTime;
inline unsigned int currentFrameIRaw() { inline unsigned int currentFrameIRaw() {
if (fps <= 0) return lastFrame; if (fps <= 0) return lastFrame;
return floor(lastFrame + (playTime / ((1 / fps) * 1000000))); return floor(lastFrame + (playTime / (1 / fps)));
} }
unsigned int currentFrameI() { unsigned int currentFrameI() {
@ -2104,6 +2103,24 @@ 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::releaseResources() void Bitmap::releaseResources()
{ {
if (p->megaSurface) if (p->megaSurface)

View file

@ -158,6 +158,8 @@ public:
sigslot::signal<> modified; sigslot::signal<> modified;
static int maxSize(); static int maxSize();
bool invalid() const;
private: private:
void releaseResources(); void releaseResources();

View file

@ -797,7 +797,7 @@ struct GraphicsPrivate {
int frameCount; int frameCount;
int brightness; int brightness;
unsigned long long last_update; double last_update;
FPSLimiter fpsLimiter; FPSLimiter fpsLimiter;
@ -816,8 +816,8 @@ struct GraphicsPrivate {
bool integerScaleActive; bool integerScaleActive;
bool integerLastMileScaling; bool integerLastMileScaling;
std::vector<unsigned long long> avgFPSData; std::vector<double> avgFPSData;
unsigned long long last_avg_update; double last_avg_update;
SDL_mutex *avgFPSLock; SDL_mutex *avgFPSLock;
SDL_mutex *glResourceLock; SDL_mutex *glResourceLock;
@ -837,7 +837,7 @@ struct GraphicsPrivate {
last_update(0), last_avg_update(0), backingScaleFactor(1), integerScaleFactor(0, 0), last_update(0), last_avg_update(0), backingScaleFactor(1), integerScaleFactor(0, 0),
integerScaleActive(rtData->config.integerScaling.active), integerScaleActive(rtData->config.integerScaling.active),
integerLastMileScaling(rtData->config.integerScaling.lastMileScaling) { integerLastMileScaling(rtData->config.integerScaling.lastMileScaling) {
avgFPSData = std::vector<unsigned long long>(); avgFPSData = std::vector<double>();
avgFPSLock = SDL_CreateMutex(); avgFPSLock = SDL_CreateMutex();
glResourceLock = SDL_CreateMutex(); glResourceLock = SDL_CreateMutex();
@ -1079,7 +1079,7 @@ struct GraphicsPrivate {
if (avgFPSData.size() > 40) if (avgFPSData.size() > 40)
avgFPSData.erase(avgFPSData.begin()); avgFPSData.erase(avgFPSData.begin());
unsigned long long time = shState->runTime(); double time = shState->runTime();
avgFPSData.push_back(time - last_avg_update); avgFPSData.push_back(time - last_avg_update);
last_avg_update = time; last_avg_update = time;
SDL_UnlockMutex(avgFPSLock); SDL_UnlockMutex(avgFPSLock);
@ -1102,10 +1102,10 @@ struct GraphicsPrivate {
double averageFPS() { double averageFPS() {
double ret = 0; double ret = 0;
SDL_LockMutex(avgFPSLock); SDL_LockMutex(avgFPSLock);
for (unsigned long long times : avgFPSData) for (double times : avgFPSData)
ret += times; ret += times;
ret = 1 / (ret / avgFPSData.size() / 1000000); ret = 1 / (ret / avgFPSData.size());
SDL_UnlockMutex(avgFPSLock); SDL_UnlockMutex(avgFPSLock);
return ret; return ret;
} }
@ -1138,11 +1138,11 @@ Graphics::Graphics(RGSSThreadData *data) {
Graphics::~Graphics() { delete p; } Graphics::~Graphics() { delete p; }
unsigned long long Graphics::getDelta() { double Graphics::getDelta() {
return shState->runTime() - p->last_update; return shState->runTime() - p->last_update;
} }
unsigned long long Graphics::lastUpdate() { double Graphics::lastUpdate() {
return p->last_update; return p->last_update;
} }

View file

@ -36,8 +36,8 @@ struct Movie;
class Graphics class Graphics
{ {
public: public:
unsigned long long getDelta(); double getDelta();
unsigned long long lastUpdate(); double lastUpdate();
void update(bool checkForShutdown = true); void update(bool checkForShutdown = true);
void freeze(); void freeze();

View file

@ -188,6 +188,9 @@ struct SpritePrivate
if (nullOrDisposed(bitmap)) if (nullOrDisposed(bitmap))
return; return;
if (bitmap->invalid())
return;
if (!opacity) if (!opacity)
return; return;

View file

@ -707,14 +707,12 @@ struct InputPrivate
unsigned int rawRepeatCount; unsigned int rawRepeatCount;
unsigned int buttonRepeatCount; unsigned int buttonRepeatCount;
unsigned long long repeatTime; double repeatTime, rawRepeatTime, buttonRepeatTime;
unsigned long long rawRepeatTime;
unsigned long long buttonRepeatTime;
unsigned int repeatStart; unsigned int repeatStart;
unsigned int repeatDelay; unsigned int repeatDelay;
unsigned long long last_update; double last_update;
int vScrollDistance; int vScrollDistance;
@ -1185,7 +1183,7 @@ Input::Input(const RGSSThreadData &rtData)
p = new InputPrivate(rtData); p = new InputPrivate(rtData);
} }
unsigned long long Input::getDelta() { double Input::getDelta() {
return shState->runTime() - p->last_update; return shState->runTime() - p->last_update;
} }
@ -1301,7 +1299,7 @@ unsigned int Input::count(int button) {
return p->repeatCount; return p->repeatCount;
} }
unsigned long long Input::repeatTime(int button) { double Input::repeatTime(int button) {
if (button != p->repeating) if (button != p->repeating)
return 0; return 0;
@ -1367,7 +1365,7 @@ unsigned int Input::controllerRepeatcount(int button) {
return p->buttonRepeatCount; return p->buttonRepeatCount;
} }
unsigned long long Input::repeatTimeEx(int code, bool isVKey) { double Input::repeatTimeEx(int code, bool isVKey) {
int c = code; int c = code;
if (isVKey) { if (isVKey) {
try { c = vKeyToScancode[code]; } try { c = vKeyToScancode[code]; }
@ -1380,7 +1378,7 @@ unsigned long long Input::repeatTimeEx(int code, bool isVKey) {
return shState->runTime() - p->rawRepeatTime; return shState->runTime() - p->rawRepeatTime;
} }
unsigned long long Input::controllerRepeatTimeEx(int button) { double Input::controllerRepeatTimeEx(int button) {
if (button != p->buttonRepeating) if (button != p->buttonRepeating)
return 0; return 0;

View file

@ -58,7 +58,7 @@ public:
void recalcRepeat(unsigned int fps); void recalcRepeat(unsigned int fps);
unsigned long long getDelta(); double getDelta();
void update(); void update();
std::vector<std::string> getBindings(ButtonCode code); std::vector<std::string> getBindings(ButtonCode code);
@ -68,21 +68,21 @@ public:
bool isRepeated(int button); bool isRepeated(int button);
bool isReleased(int button); bool isReleased(int button);
unsigned int count(int button); unsigned int count(int button);
unsigned long long repeatTime(int button); double repeatTime(int button);
bool isPressedEx(int code, bool isVKey); bool isPressedEx(int code, bool isVKey);
bool isTriggeredEx(int code, bool isVKey); bool isTriggeredEx(int code, bool isVKey);
bool isRepeatedEx(int code, bool isVKey); bool isRepeatedEx(int code, bool isVKey);
bool isReleasedEx(int code, bool isVKey); bool isReleasedEx(int code, bool isVKey);
unsigned int repeatcount(int code, bool isVKey); unsigned int repeatcount(int code, bool isVKey);
unsigned long long repeatTimeEx(int code, bool isVKey); double repeatTimeEx(int code, bool isVKey);
bool controllerIsPressedEx(int button); bool controllerIsPressedEx(int button);
bool controllerIsTriggeredEx(int button); bool controllerIsTriggeredEx(int button);
bool controllerIsRepeatedEx(int button); bool controllerIsRepeatedEx(int button);
bool controllerIsReleasedEx(int button); bool controllerIsReleasedEx(int button);
unsigned int controllerRepeatcount(int button); unsigned int controllerRepeatcount(int button);
unsigned long long controllerRepeatTimeEx(int button); double controllerRepeatTimeEx(int button);
uint8_t *rawKeyStates(); uint8_t *rawKeyStates();
unsigned int rawKeyStatesLength(); unsigned int rawKeyStatesLength();

View file

@ -363,10 +363,10 @@ Font &SharedState::defaultFont() const
return *p->defaultFont; return *p->defaultFont;
} }
unsigned long long SharedState::runTime() { double SharedState::runTime() {
if (!p) return 0; if (!p) return 0;
const auto now = std::chrono::steady_clock::now(); const auto now = std::chrono::steady_clock::now();
return std::chrono::duration_cast<std::chrono::microseconds>(now - p->startupTime).count(); return std::chrono::duration_cast<std::chrono::microseconds>(now - p->startupTime).count() / 1000.0 / 1000.0;
} }
unsigned int SharedState::genTimeStamp() unsigned int SharedState::genTimeStamp()

View file

@ -86,7 +86,7 @@ struct SharedState
unsigned int genTimeStamp(); unsigned int genTimeStamp();
// Returns time since SharedState was constructed in microseconds // Returns time since SharedState was constructed in microseconds
unsigned long long runTime(); double runTime();
/* Returns global quad IBO, and ensures it has indices /* Returns global quad IBO, and ensures it has indices
* for at least minSize quads */ * for at least minSize quads */