Keep map tiles out of the display server's GPU memory

Opening the map crashed Xwayland inside Mesa's Intel driver, which took
the server, the game client and every other X window down with it.
Map tiles and noise frames were left for Java2D to copy into textures
inside the display server, hundreds of them for one map. They are now
drawn straight from memory again, like before the render rework.

Model used: Claude Opus 5.5
This commit is contained in:
piexel
2026-10-19 11:14:37 +02:00
parent b1445430bb
commit f44ce5b327
3 changed files with 16 additions and 11 deletions
@@ -36,6 +36,7 @@ import java.awt.EventQueue;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.desktop.QuitResponse;
import java.awt.image.BufferedImage;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
@@ -75,6 +76,18 @@ public final class GuiHost {
return frame;
}
/**
* Wraps rendered pixels in an image Java2D draws straight from memory. Left to itself Java2D copies every image it
* draws into a texture inside the display server; with a map's hundreds of tiles that crashed Xwayland's Intel driver
* and took every X window on the desktop down with it.
*/
public static BufferedImage uncachedImage(int[] pixels, int width, int height) {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
image.getRaster().setDataElements(0, 0, width, height, pixels);
image.setAccelerationPriority(0F);
return image;
}
// A scaled-up default size can be bigger than a small or low resolution screen.
static Dimension fitToScreen(Dimension preferred) {
Rectangle usable = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds();
@@ -331,10 +331,8 @@ final class NoiseRenderCoordinator implements AutoCloseable {
return remainingBands.decrementAndGet() == 0;
}
// Copied into a fresh image instead of drawing into its buffer, so Java2D can keep the finished frame on the GPU.
private Result result() {
BufferedImage image = new BufferedImage(outputWidth, outputHeight, BufferedImage.TYPE_INT_RGB);
image.getRaster().setDataElements(0, 0, outputWidth, outputHeight, pixels);
BufferedImage image = GuiHost.uncachedImage(pixels, outputWidth, outputHeight);
RenderStats combined = combine(bandStats);
double milliseconds = (System.nanoTime() - startedNanos) / 1_000_000D;
return new Result(request, image, milliseconds, combined.samples, combined.minimum, combined.maximum,
@@ -18,6 +18,7 @@
package com.volmit.iris.core.gui.components;
import com.volmit.iris.core.gui.GuiHost;
import com.volmit.iris.engine.IrisComplex;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.object.IrisBiome;
@@ -98,14 +99,7 @@ public final class IrisRenderer {
checkCancelled(cancelled);
int[] pixels = renderPixels(sx, sz, size, resolution, currentType, cancelled, studio);
return managedImage(pixels, resolution);
}
// Filled through the raster rather than its backing array, so Java2D may keep the image on the GPU between paints.
private static BufferedImage managedImage(int[] pixels, int resolution) {
BufferedImage image = new BufferedImage(resolution, resolution, BufferedImage.TYPE_INT_RGB);
image.getRaster().setDataElements(0, 0, resolution, resolution, pixels);
return image;
return GuiHost.uncachedImage(pixels, resolution, resolution);
}
private int[] renderPixels(