mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-26 14:00:26 +00:00
Show 3D frames only once panels over them are painted
- A frame drawn into a 3D view's window waits until Swing has painted what lies behind the floating panels, so the ground behind the height slider no longer trails the view while it moves Model used: Claude Opus 5.5
This commit is contained in:
1 parent
54213d4b62
commit
2acfac36fc
3 files changed
+58
-25
No files matched your search
@@ -77,7 +77,7 @@ final class GpuFrames {
|
|||||||
if (!windows.get().drawInto(window)) {
|
if (!windows.get().drawInto(window)) {
|
||||||
throw new IllegalStateException("GLX can't draw into the view's window");
|
throw new IllegalStateException("GLX can't draw into the view's window");
|
||||||
}
|
}
|
||||||
return drawing.drawInto(new GpuWindowFrame(windows.get(), window, screen.seeThrough()));
|
return drawing.drawInto(new GpuWindowFrame(screen.seeThrough()));
|
||||||
} catch (RuntimeException failure) {
|
} catch (RuntimeException failure) {
|
||||||
gpu.stopDrawingIntoWindows(failure);
|
gpu.stopDrawingIntoWindows(failure);
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
@@ -85,6 +85,31 @@ final class GpuFrames {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Puts the last frame drawn into the view's window on screen. Another view may have drawn into its own window
|
||||||
|
* since, so the view's window is made current again first.
|
||||||
|
*
|
||||||
|
* @return false when the window didn't take it, and the caller should draw a picture instead
|
||||||
|
*/
|
||||||
|
static boolean showOnScreen(ScreenTarget screen) {
|
||||||
|
return onCard(gpu -> {
|
||||||
|
Optional<GlxDisplay> windows = gpu.windows();
|
||||||
|
long window = screen.canvas().window();
|
||||||
|
if (windows.isEmpty() || window == 0L) {
|
||||||
|
return Optional.<Boolean>empty();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (!windows.get().drawInto(window) || !windows.get().show(window)) {
|
||||||
|
throw new IllegalStateException("The window didn't take the frame");
|
||||||
|
}
|
||||||
|
return Optional.of(true);
|
||||||
|
} catch (RuntimeException failure) {
|
||||||
|
gpu.stopDrawingIntoWindows(failure);
|
||||||
|
return Optional.<Boolean>empty();
|
||||||
|
}
|
||||||
|
}).isPresent();
|
||||||
|
}
|
||||||
|
|
||||||
static void failOnError() {
|
static void failOnError() {
|
||||||
int error = glGetError();
|
int error = glGetError();
|
||||||
if (error != GL_NO_ERROR) {
|
if (error != GL_NO_ERROR) {
|
||||||
@@ -119,7 +144,7 @@ final class GpuFrames {
|
|||||||
return offScreen;
|
return offScreen;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Optional<OrbitPicture> onCard(CardWork work) {
|
private static <T> Optional<T> onCard(CardWork<T> work) {
|
||||||
if (failed) {
|
if (failed) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
@@ -145,7 +170,7 @@ final class GpuFrames {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
private interface CardWork {
|
private interface CardWork<T> {
|
||||||
Optional<OrbitPicture> drawOn(GpuContext gpu);
|
Optional<T> drawOn(GpuContext gpu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -28,13 +28,12 @@ import static org.lwjgl.opengl.GL30.*;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A frame drawn straight into a view's window. Of what was drawn, only the patches Swing paints where panels float
|
* A frame drawn straight into a view's window. Of what was drawn, only the patches Swing paints where panels float
|
||||||
* over the window are read back. Only for the GPU thread.
|
* over the window are read back. The frame waits in the window's back buffer until {@link GpuFrames#showOnScreen},
|
||||||
|
* so it can go on screen together with its patches. Only for the GPU thread.
|
||||||
*/
|
*/
|
||||||
final class GpuWindowFrame implements GpuFrameTarget {
|
final class GpuWindowFrame implements GpuFrameTarget {
|
||||||
private static final int WINDOW_FRAMEBUFFER = 0;
|
private static final int WINDOW_FRAMEBUFFER = 0;
|
||||||
|
|
||||||
private final GlxDisplay windows;
|
|
||||||
private final long window;
|
|
||||||
private final List<Rectangle> seeThrough;
|
private final List<Rectangle> seeThrough;
|
||||||
private int width;
|
private int width;
|
||||||
private int height;
|
private int height;
|
||||||
@@ -42,9 +41,7 @@ final class GpuWindowFrame implements GpuFrameTarget {
|
|||||||
/**
|
/**
|
||||||
* @param seeThrough the parts of the view panels float over, which Swing paints instead of the window
|
* @param seeThrough the parts of the view panels float over, which Swing paints instead of the window
|
||||||
*/
|
*/
|
||||||
GpuWindowFrame(GlxDisplay windows, long window, List<Rectangle> seeThrough) {
|
GpuWindowFrame(List<Rectangle> seeThrough) {
|
||||||
this.windows = windows;
|
|
||||||
this.window = window;
|
|
||||||
this.seeThrough = seeThrough;
|
this.seeThrough = seeThrough;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,12 +59,7 @@ final class GpuWindowFrame implements GpuFrameTarget {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OrbitPicture finish(OrbitProjection projection, double depthRange) {
|
public OrbitPicture finish(OrbitProjection projection, double depthRange) {
|
||||||
OrbitPicture picture = OrbitPicture.onScreen(width, height, readPatches(),
|
return OrbitPicture.onScreen(width, height, readPatches(), GpuFrameTarget.depthInMiddle(projection, depthRange, width, height));
|
||||||
GpuFrameTarget.depthInMiddle(projection, depthRange, width, height));
|
|
||||||
if (!windows.show(window)) {
|
|
||||||
throw new IllegalStateException("The window didn't take the frame");
|
|
||||||
}
|
|
||||||
return picture;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<PicturePatch> readPatches() {
|
private List<PicturePatch> readPatches() {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import java.awt.Graphics2D;
|
|||||||
import java.awt.GraphicsConfiguration;
|
import java.awt.GraphicsConfiguration;
|
||||||
import java.awt.Point;
|
import java.awt.Point;
|
||||||
import java.awt.RenderingHints;
|
import java.awt.RenderingHints;
|
||||||
|
import java.awt.Toolkit;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import java.awt.event.ComponentAdapter;
|
import java.awt.event.ComponentAdapter;
|
||||||
@@ -338,7 +339,7 @@ final class OrbitViewport extends JComponent {
|
|||||||
renderThread.execute(() -> {
|
renderThread.execute(() -> {
|
||||||
OrbitPicture drawn = draw(frameScene, screen, frameCamera, width, height, covered);
|
OrbitPicture drawn = draw(frameScene, screen, frameCamera, width, height, covered);
|
||||||
boolean missedScreen = screen != null && !drawn.onScreen();
|
boolean missedScreen = screen != null && !drawn.onScreen();
|
||||||
EventQueue.invokeLater(() -> finishFrame(frameScene, frameCamera, drawn, missedScreen, requestedAtNanos));
|
EventQueue.invokeLater(() -> finishFrame(frameScene, frameCamera, screen, drawn, missedScreen, requestedAtNanos));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,25 +381,40 @@ final class OrbitViewport extends JComponent {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Until the card is opened, nothing knows whether frames can go into the window; the first picture opens it. A
|
* Until the card is opened, nothing knows whether frames can go into the window; the first picture opens it. A
|
||||||
* frame that should have gone into the window but couldn't isn't tried again right away. The next frame is asked
|
* frame that should have gone into the window but couldn't isn't tried again right away. A frame for a scene
|
||||||
* for before this one's patches are painted, so the card draws while Swing paints.
|
* replaced meanwhile is never shown.
|
||||||
*/
|
*/
|
||||||
private void finishFrame(OrbitScene frameScene, OrbitCamera frameCamera, OrbitPicture drawn, boolean missedScreen,
|
private void finishFrame(OrbitScene frameScene, OrbitCamera frameCamera, ScreenTarget screen, OrbitPicture drawn,
|
||||||
long requestedAtNanos) {
|
boolean missedScreen, long requestedAtNanos) {
|
||||||
rendering = false;
|
rendering = false;
|
||||||
measureFrameTime(System.nanoTime() - requestedAtNanos);
|
measureFrameTime(System.nanoTime() - requestedAtNanos);
|
||||||
boolean shown = frameScene == scene;
|
if (frameScene == scene) {
|
||||||
if (shown) {
|
|
||||||
takePicture(drawn, frameCamera);
|
takePicture(drawn, frameCamera);
|
||||||
|
paintPicture(drawn);
|
||||||
|
showInWindow(screen, drawn);
|
||||||
}
|
}
|
||||||
boolean canGoOnScreen = !missedScreen && canvas != null && !canvas.isVisible() && drawsOnScreen();
|
boolean canGoOnScreen = !missedScreen && canvas != null && !canvas.isVisible() && drawsOnScreen();
|
||||||
if (renderAgain || drawn.catchingUp() || canGoOnScreen) {
|
if (renderAgain || drawn.catchingUp() || canGoOnScreen) {
|
||||||
renderAgain = false;
|
renderAgain = false;
|
||||||
requestFrame();
|
requestFrame();
|
||||||
}
|
}
|
||||||
if (shown) {
|
|
||||||
paintPicture(drawn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A frame in the window only goes on screen once Swing has painted its patches, or the panels floating over the
|
||||||
|
* window would show the frame before. Swing and the card reach the screen through separate connections, so
|
||||||
|
* Swing's is flushed first. The render thread shows the frame before it draws the next one into the window.
|
||||||
|
*/
|
||||||
|
private void showInWindow(ScreenTarget screen, OrbitPicture drawn) {
|
||||||
|
if (!drawn.onScreen()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Toolkit.getDefaultToolkit().sync();
|
||||||
|
renderThread.execute(() -> {
|
||||||
|
if (!GpuFrames.showOnScreen(screen)) {
|
||||||
|
EventQueue.invokeLater(this::requestFrame);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// A whole picture replaces the window until frames go into it again.
|
// A whole picture replaces the window until frames go into it again.
|
||||||
|
|||||||
Reference in new issue
Block a user