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_UNUSED_PARAM;
return ULL2NUM(shState->runTime());
return rb_float_new(shState->runTime());
}
RB_METHOD(mkxpDataDirectory) {

View file

@ -434,7 +434,11 @@ RB_METHOD(bitmapGetMega){
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){
@ -444,7 +448,11 @@ RB_METHOD(bitmapGetAnimated){
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){

View file

@ -37,6 +37,8 @@ disposableAddChild(VALUE disp, VALUE child)
return;
}
VALUE objID = rb_obj_id(child);
VALUE children = rb_iv_get(disp, "children");
bool exists = false;
@ -47,11 +49,11 @@ disposableAddChild(VALUE disp, VALUE child)
rb_iv_set(disp, "children", children);
}
else {
exists = RTEST(rb_funcall(children, rb_intern("include?"), 1, child));
exists = RTEST(rb_funcall(children, rb_intern("include?"), 1, objID));
}
if (!exists)
rb_ary_push(children, child);
rb_ary_push(children, objID);
GFX_UNLOCK;
}
@ -63,11 +65,13 @@ disposableRemoveChild(VALUE disp, VALUE child)
return;
}
VALUE objID = rb_obj_id(child);
VALUE children = rb_iv_get(disp, "children");
if (NIL_P(children))
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))
return;
@ -83,10 +87,16 @@ disposableDisposeChildren(VALUE disp)
if (NIL_P(children))
return;
ID dispFun = rb_intern("_mkxp_dispose_alias");
for (long i = 0; i < RARRAY_LEN(children); ++i)
rb_funcall2(rb_ary_entry(children, i), dispFun, 0, 0);
for (long i = 0; i < RARRAY_LEN(children); ++i) {
int state;
rb_protect([](VALUE args){
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>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -363,10 +363,10 @@ Font &SharedState::defaultFont() const
return *p->defaultFont;
}
unsigned long long SharedState::runTime() {
double SharedState::runTime() {
if (!p) return 0;
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()

View file

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