Ensure task queue is not locked during rendering (fixes #157)

Similar to 3fedc22 in effect and cause. However tasks are executed, they just
cannot be submitted pre-1.14 because MC would continuously hold the lock on the
scheduledTasks queue for the entire renderVideo call.
This commit is contained in:
Jonas Herzig
2020-05-07 13:42:06 +02:00
parent 316f4e2c3c
commit 72ce522813
3 changed files with 27 additions and 10 deletions

View File

@@ -475,13 +475,30 @@ public class ReplayMod implements
public final ReplayModExecutor executor = new ReplayModExecutor("Client/ReplayMod"); public final ReplayModExecutor executor = new ReplayModExecutor("Client/ReplayMod");
//#endif //#endif
// Pre-1.14 MC would hold the lock on the scheduledTasks queue while executing its tasks
// such that no new tasks could be submitted while one of them was running.
// This would cause issues with long-running tasks (e.g. video rendering) as it would
// block all async tasks (e.g. skin loading).
public void runLaterWithoutLock(Runnable runnable) {
//#if MC>=11400
// MC 1.14+ no longer synchronizes on the queue while running its tasks
runLater(runnable);
//#else
//$$ runLater(() -> runLaterWithoutLock(runnable), runnable);
//#endif
}
public void runLater(Runnable runnable) { public void runLater(Runnable runnable) {
runLater(runnable, () -> runLater(runnable));
}
private void runLater(Runnable runnable, Runnable defer) {
//#if MC>=11400 //#if MC>=11400
if (mc.isOnThread() && inRunLater && !inRenderTaskQueue) { if (mc.isOnThread() && inRunLater && !inRenderTaskQueue) {
((MinecraftAccessor) mc).getRenderTaskQueue().offer(() -> { ((MinecraftAccessor) mc).getRenderTaskQueue().offer(() -> {
inRenderTaskQueue = true; inRenderTaskQueue = true;
try { try {
runLater(runnable); defer.run();
} finally { } finally {
inRenderTaskQueue = false; inRenderTaskQueue = false;
} }
@@ -503,13 +520,13 @@ public class ReplayMod implements
//$$ @SubscribeEvent //$$ @SubscribeEvent
//$$ public void onRenderTick(TickEvent.RenderTickEvent event) { //$$ public void onRenderTick(TickEvent.RenderTickEvent event) {
//$$ if (event.phase == TickEvent.Phase.START) { //$$ if (event.phase == TickEvent.Phase.START) {
//$$ runLater(runnable);
//$$ FORGE_BUS.unregister(this); //$$ FORGE_BUS.unregister(this);
//$$ defer.run();
//$$ } //$$ }
//$$ } //$$ }
//$$ }); //$$ });
//#else //#else
//$$ FML_BUS.register(new RunLaterHelper(runnable)); //$$ FML_BUS.register(new RunLaterHelper(defer));
//#endif //#endif
//$$ return; //$$ return;
//$$ } //$$ }
@@ -539,17 +556,17 @@ public class ReplayMod implements
//$$ //$$
//$$ // in 1.7.10 apparently events can't be delivered to anonymous classes //$$ // in 1.7.10 apparently events can't be delivered to anonymous classes
//$$ public class RunLaterHelper { //$$ public class RunLaterHelper {
//$$ private final Runnable runnable; //$$ private final Runnable defer;
//$$ //$$
//$$ private RunLaterHelper(Runnable runnable) { //$$ private RunLaterHelper(Runnable defer) {
//$$ this.runnable = runnable; //$$ this.defer = defer;
//$$ } //$$ }
//$$ //$$
//$$ @SubscribeEvent //$$ @SubscribeEvent
//$$ public void onRenderTick(TickEvent.RenderTickEvent event) { //$$ public void onRenderTick(TickEvent.RenderTickEvent event) {
//$$ if (event.phase == TickEvent.Phase.START) { //$$ if (event.phase == TickEvent.Phase.START) {
//$$ runLater(runnable);
//$$ FML_BUS.unregister(this); //$$ FML_BUS.unregister(this);
//$$ defer.run();
//$$ } //$$ }
//$$ } //$$ }
//$$ } //$$ }

View File

@@ -197,7 +197,7 @@ public class GuiRenderQueue extends AbstractGuiPopup<GuiRenderQueue> {
renderButton.onClick(() -> { renderButton.onClick(() -> {
LOGGER.trace("Render button clicked"); LOGGER.trace("Render button clicked");
ReplayMod.instance.runLater(() -> processQueue(queue)); ReplayMod.instance.runLaterWithoutLock(() -> processQueue(queue));
}); });
updateButtons(); updateButtons();
@@ -233,7 +233,7 @@ public class GuiRenderQueue extends AbstractGuiPopup<GuiRenderQueue> {
// Update current job with fixed ffmpeg arguments // Update current job with fixed ffmpeg arguments
renderJob.setSettings(newSettings); renderJob.setSettings(newSettings);
// Restart queue, skipping the already completed jobs // Restart queue, skipping the already completed jobs
processQueue(Iterables.skip(queue, jobsToSkip)); ReplayMod.instance.runLaterWithoutLock(() -> processQueue(Iterables.skip(queue, jobsToSkip)));
}); });
return; return;
} catch (Throwable t) { } catch (Throwable t) {

View File

@@ -229,7 +229,7 @@ public class GuiRenderSettings extends GuiScreen implements Closeable {
new GuiRenderQueue(GuiRenderSettings.this, GuiRenderSettings.this, replayHandler, timeline).open(); new GuiRenderQueue(GuiRenderSettings.this, GuiRenderSettings.this, replayHandler, timeline).open();
} }
}).setSize(100, 20).setI18nLabel("replaymod.gui.renderqueue.open"); }).setSize(100, 20).setI18nLabel("replaymod.gui.renderqueue.open");
public final GuiButton renderButton = new GuiButton(buttonPanel).onClick(() -> ReplayMod.instance.runLater(new Runnable() { public final GuiButton renderButton = new GuiButton(buttonPanel).onClick(() -> ReplayMod.instance.runLaterWithoutLock(new Runnable() {
@Override @Override
public void run() { public void run() {
// Closing this GUI ensures that settings are saved // Closing this GUI ensures that settings are saved