Conductor Multithreading + in-game frame drops fix
This commit is contained in:
@@ -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
@@ -1 +1 @@
|
||||
1863
|
||||
1873
|
||||
@@ -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
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user