Mojira Archive
MC-33383

Lag while taking screenshot with F2 in singleplayer

The bug

While taking a screenshot using the in-game F2 key, the game freezes for a second and does not take an accurate screenshot.

Code analysis

The reason why this is happening is because saving the screenshot is not done by a seperate thread. This means the lag time is determined by the writing speed of your harddrive.
Another reason which has probably a rather small impact is that in case frame buffering is enabled Minecraft iterates over all pixels in the screenshot and sets them in a BufferedImage. I do not see any advantage of this.
Here is a "fix" that solves this problem, however the ScreenshotSavingThread is only used as a local variable to demonstrate that it solves the problem. It should be rather in the class itself and also set the error message in case it fails:
saveScreenshot(File p_148259_0_, String p_148259_1_, int p_148259_2_, int p_148259_3_, Framebuffer p_148259_4_) method of the net.minecraft.util.ScreenShotHelper class (MCP 1.8 names)

saveScreenshot(File p_148259_0_, String p_148259_1_, int p_148259_2_, int p_148259_3_, Framebuffer p_148259_4_)
// Is there any advantage from this?
if (OpenGlHelper.isFramebufferEnabled())
{
	var7 = new BufferedImage(p_148259_4_.framebufferWidth, p_148259_4_.framebufferHeight, 1);
	int var8 = p_148259_4_.framebufferTextureHeight - p_148259_4_.framebufferHeight;

	for (int var9 = var8; var9 < p_148259_4_.framebufferTextureHeight; ++var9)
	{
		for (int var10 = 0; var10 < p_148259_4_.framebufferWidth; ++var10)
		{
			var7.setRGB(var10, var9 - var8, pixelValues[var9 * p_148259_4_.framebufferTextureWidth + var10]);
		}
	}
}
else
{
	var7 = new BufferedImage(p_148259_2_, p_148259_3_, 1);
	var7.setRGB(0, 0, p_148259_2_, p_148259_3_, pixelValues, 0, p_148259_2_);
}

File var12;

if (p_148259_1_ == null)
{
	var12 = getTimestampedPNGFileForDirectory(var5);
}
else
{
	var12 = new File(var5, p_148259_1_);
}

// Create an empty file to make sure a ScreenshotSavingThreads do not override it by accident
var12.createNewFile();

//ImageIO.write(var7, "png", var12);
class ScreenshotSavingThread extends Thread {
	private final BufferedImage bufferedImage;
	private final File file;
	
	private ScreenshotSavingThread(BufferedImage bufferedImage, File file) {
		this.bufferedImage = bufferedImage;
		this.file = file;
	}
	
	@Override
	public void run() {
		try {
			ImageIO.write(bufferedImage, "png", file);
		} catch (IOException e) {
			// TODO Print the error message in chat and in the log
		}
	}
}
new ScreenshotSavingThread(var7, var12).start();
Flaws with this design
  • Stopping Minecraft could cause half-finished screenshots (unlikely); thread could be non-daemon
  • Taking screenshots to often results in a OutOfMemoryError (unlikely, that would need with 500KB per screenshot and 500MB allocated RAM about 1000 ScreenshotSavingThreads running at once)
  • Reporting error or success would have to be synchronized and would appear delayed which could be confusing