Conductor Multithreading + in-game frame drops fix

This commit is contained in:
Yoshman29
2022-12-30 18:26:39 +01:00
parent 5cded1a197
commit 48bad77396
7 changed files with 115 additions and 33 deletions
+3
View File
@@ -111,6 +111,9 @@
Reduces compilation time at the cost of limited HScript flexibility. !-->
<define name="COMPILE_ALL_CLASSES" />
<!-- Comment this out to disable multithreading !-->
<haxedef name="ALLOW_MULTITHREADING" unless="web || flash" />
<!-- Comment this out to disable titlescreen.xml !-->
<haxedef name="TITLESCREEN_XML" />
+1 -1
View File
@@ -1 +1 @@
1863
1873
+12 -10
View File
@@ -1516,7 +1516,9 @@ class PlayState extends MusicBeatState
if (event.showRating || (event.showRating == null && event.player && !note.isSustainNote))
{
var rating:FlxSprite = new FlxSprite(-40, -60);
var rating:FlxSprite = comboGroup.recycle(FlxSprite);
rating.resetSprite(-40, -60);
comboGroup.remove(rating, true);
songScore += score;
@@ -1525,7 +1527,9 @@ class PlayState extends MusicBeatState
rating.velocity.y -= FlxG.random.int(140, 175);
rating.velocity.x -= FlxG.random.int(0, 10);
var comboSpr:FlxSprite = new FlxSprite().loadAnimatedGraphic(Paths.image('${event.ratingPrefix}combo${event.ratingSuffix}'));
var comboSpr:FlxSprite = comboGroup.recycle(FlxSprite).loadAnimatedGraphic(Paths.image('${event.ratingPrefix}combo${event.ratingSuffix}'));
comboSpr.resetSprite(0, 0);
comboGroup.remove(comboSpr, true);
comboSpr.acceleration.y = 600;
comboSpr.velocity.y -= 150;
comboSpr.velocity.x += FlxG.random.int(1, 10);
@@ -1548,7 +1552,9 @@ class PlayState extends MusicBeatState
{
var e = separatedScore.charAt(i);
var numScore:FlxSprite = new FlxSprite((43 * i) - 90, 80).loadAnimatedGraphic(Paths.image('${event.ratingPrefix}num$e${event.ratingSuffix}'));
var numScore:FlxSprite = comboGroup.recycle(FlxSprite).loadAnimatedGraphic(Paths.image('${event.ratingPrefix}num$e${event.ratingSuffix}'));
numScore.resetSprite((43 * i) - 90, 80);
comboGroup.remove(numScore, true);
numScore.antialiasing = event.numAntialiasing;
numScore.scale.set(event.numScale, event.numScale);
numScore.updateHitbox();
@@ -1562,8 +1568,7 @@ class PlayState extends MusicBeatState
FlxTween.tween(numScore, {alpha: 0}, 0.2, {
onComplete: function(tween:FlxTween)
{
comboGroup.remove(numScore, true);
numScore.destroy();
numScore.exists = false;
},
startDelay: Conductor.crochet * 0.002
});
@@ -1578,11 +1583,8 @@ class PlayState extends MusicBeatState
FlxTween.tween(comboSpr, {alpha: 0}, 0.2, {
onComplete: function(tween:FlxTween)
{
comboGroup.remove(comboSpr, true);
comboGroup.remove(rating, true);
comboSpr.destroy();
rating.destroy();
rating.exists = false;
comboSpr.exists = false;
},
startDelay: Conductor.crochet * 0.001
});
+52 -18
View File
@@ -76,6 +76,7 @@ class Conductor
@:dox(hide) public static var lastSongPos:Float = 0;
@:dox(hide) public static var lastSongPosTime:Float = 0;
@:dox(hide) public static var speed:Float = 0;
@:dox(hide) public static var destSpeed:Float = 0;
@:dox(hide) public static var offset:Float = 0;
@:dox(hide) public static var safeZoneOffset:Float = 175; // is calculated in create(), is safeFrames in milliseconds
@@ -85,6 +86,14 @@ class Conductor
*/
public static var bpmChangeMap:Array<BPMChangeEvent> = [];
/**
* Thread for multi-threaded audio syncing.
*/
#if ALLOW_MULTITHREADING
public static var syncThread:sys.thread.Thread;
public static var syncThreadTime:Null<Float> = null;
#end
@:dox(hide) public function new() {}
public static function reset() {
@@ -130,10 +139,50 @@ class Conductor
}
}
private static var elapsed:Float;
public static function init() {
FlxG.signals.preUpdate.add(update);
FlxG.signals.preStateCreate.add(onStateSwitch);
reset();
#if ALLOW_MULTITHREADING
syncThread = ThreadUtil.createSafe(function() {
while(true) {
if (syncThreadTime == null)
syncThreadTime = Sys.time();
// if (FlxG.state != null && FlxG.state is MusicBeatState && cast(FlxG.state, MusicBeatState).cancelConductorUpdate) continue;
elapsed = -(syncThreadTime - (syncThreadTime = Sys.time()));
if (elapsed == 0) continue;
__updateSongPos(elapsed);
}
}, true);
#end
}
private static function __updateSongPos(elapsed:Float) {
if (FlxG.sound.music == null || !FlxG.sound.music.playing) {
speed = destSpeed = 1;
lastSongPos = FlxG.sound.music != null ? FlxG.sound.music.time : 0;
lastSongPosTime = Main.time;
return;
}
var lastPos = lastSongPos;
if (lastSongPos != (lastSongPos = FlxG.sound.music.time)) {
// update conductor
var timeUntilUpdate = -(lastSongPosTime - (lastSongPosTime = Main.time));
var elapsedAL = (lastSongPos - lastPos);
destSpeed = FlxMath.bound(timeUntilUpdate / elapsedAL, 0.925, 1.075);
songPosition = lastSongPos;
} else {
songPosition += elapsed * 1000 * speed;
}
speed = FlxMath.lerp(speed, destSpeed, FlxMath.bound(elapsed, 0, 1));
}
private static function onStateSwitch(newState:FlxState) {
@@ -141,26 +190,11 @@ class Conductor
reset();
}
private static function update() {
var elapsed = FlxG.elapsed;
if (FlxG.sound.music == null || !FlxG.sound.music.playing) {
speed = 1;
lastSongPos = FlxG.sound.music != null ? FlxG.sound.music.time : 0;
lastSongPosTime = Main.time;
return;
}
if (FlxG.state != null && FlxG.state is MusicBeatState && cast(FlxG.state, MusicBeatState).cancelConductorUpdate) return;
var lastPos = lastSongPos;
if (lastSongPos != (lastSongPos = FlxG.sound.music.time)) {
// update conductor
var timeUntilUpdate = -(lastSongPosTime - (lastSongPosTime = Main.time));
var elapsedAL = (lastSongPos - lastPos);
speed = FlxMath.bound(FlxMath.lerp(speed, timeUntilUpdate / elapsedAL, FlxMath.bound(timeUntilUpdate / 1000, 0, 1)), 0.925, 1.075);
songPosition = lastSongPos;
} else {
songPosition += elapsed * 1000 * speed;
}
#if !ALLOW_MULTITHREADING
__updateSongPos(FlxG.elapsed);
#end
if (bpm > 0) {
// updates curbeat and stuff
+9
View File
@@ -342,4 +342,13 @@ class CoolUtil
color1.redMultiplier = color2.redMultiplier;
color1.redOffset = color2.redOffset;
}
public static function resetSprite(spr:FlxSprite, x:Float, y:Float) {
spr.reset(x, y);
spr.alpha = 1;
spr.visible = true;
spr.active = true;
spr.antialiasing = FlxSprite.defaultAntialiasing;
spr.rotOffset.set();
}
}
+6 -4
View File
@@ -28,8 +28,10 @@ import funkin.system.Discord.DiscordClient;
#end
import lime.app.Application;
#if sys
#if ALLOW_MULTITHREADING
import sys.thread.Thread;
#end
#if sys
import sys.io.File;
#end
// TODO: REMOVE TEST
@@ -51,7 +53,7 @@ class Main extends Sprite
// You can pretty much ignore everything from here on - your code should go in your states.
#if sys
#if ALLOW_MULTITHREADING
public static var gameThreads:Array<Thread> = [];
#end
@@ -120,7 +122,7 @@ class Main extends Sprite
private static var __threadCycle:Int = 0;
public static function execAsync(func:Void->Void) {
#if sys
#if ALLOW_MULTITHREADING
var thread = gameThreads[(__threadCycle++) % gameThreads.length];
thread.events.run(func);
#else
@@ -135,7 +137,7 @@ class Main extends Sprite
public function loadGameSettings() {
@:privateAccess
FlxG.game.getTimer = getTimer;
#if sys
#if ALLOW_MULTITHREADING
for(i in 0...4)
gameThreads.push(Thread.createWithEventLoop(function() {Thread.current().events.promise();}));
#end
+32
View File
@@ -0,0 +1,32 @@
package funkin.system;
#if ALLOW_MULTITHREADING
class ThreadUtil {
/**
* Creates a new Thread with an error handler.
* @param func Function to execute
* @param autoRestart Whenever the thread should auto restart itself after crashing.
*/
public static function createSafe(func:Void->Void, autoRestart:Bool = false) {
if (autoRestart) {
return sys.thread.Thread.create(function() {
while(true) {
try {
func();
} catch(e) {
trace(e.details());
}
}
});
} else {
return sys.thread.Thread.create(function() {
try {
func();
} catch(e) {
trace(e.details());
}
});
}
}
}
#end