mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-26 05:50:39 +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)) {
|
||||
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) {
|
||||
gpu.stopDrawingIntoWindows(failure);
|
||||
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() {
|
||||
int error = glGetError();
|
||||
if (error != GL_NO_ERROR) {
|
||||
@@ -119,7 +144,7 @@ final class GpuFrames {
|
||||
return offScreen;
|
||||
}
|
||||
|
||||
private static Optional<OrbitPicture> onCard(CardWork work) {
|
||||
private static <T> Optional<T> onCard(CardWork<T> work) {
|
||||
if (failed) {
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -145,7 +170,7 @@ final class GpuFrames {
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface CardWork {
|
||||
Optional<OrbitPicture> drawOn(GpuContext gpu);
|
||||
private interface CardWork<T> {
|
||||
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
|
||||
* 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 {
|
||||
private static final int WINDOW_FRAMEBUFFER = 0;
|
||||
|
||||
private final GlxDisplay windows;
|
||||
private final long window;
|
||||
private final List<Rectangle> seeThrough;
|
||||
private int width;
|
||||
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
|
||||
*/
|
||||
GpuWindowFrame(GlxDisplay windows, long window, List<Rectangle> seeThrough) {
|
||||
this.windows = windows;
|
||||
this.window = window;
|
||||
GpuWindowFrame(List<Rectangle> seeThrough) {
|
||||
this.seeThrough = seeThrough;
|
||||
}
|
||||
|
||||
@@ -62,12 +59,7 @@ final class GpuWindowFrame implements GpuFrameTarget {
|
||||
|
||||
@Override
|
||||
public OrbitPicture finish(OrbitProjection projection, double depthRange) {
|
||||
OrbitPicture picture = OrbitPicture.onScreen(width, height, readPatches(),
|
||||
GpuFrameTarget.depthInMiddle(projection, depthRange, width, height));
|
||||
if (!windows.show(window)) {
|
||||
throw new IllegalStateException("The window didn't take the frame");
|
||||
}
|
||||
return picture;
|
||||
return OrbitPicture.onScreen(width, height, readPatches(), GpuFrameTarget.depthInMiddle(projection, depthRange, width, height));
|
||||
}
|
||||
|
||||
private List<PicturePatch> readPatches() {
|
||||
|
||||
@@ -31,6 +31,7 @@ import java.awt.Graphics2D;
|
||||
import java.awt.GraphicsConfiguration;
|
||||
import java.awt.Point;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.ComponentAdapter;
|
||||
@@ -338,7 +339,7 @@ final class OrbitViewport extends JComponent {
|
||||
renderThread.execute(() -> {
|
||||
OrbitPicture drawn = draw(frameScene, screen, frameCamera, width, height, covered);
|
||||
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
|
||||
* frame that should have gone into the window but couldn't isn't tried again right away. The next frame is asked
|
||||
* for before this one's patches are painted, so the card draws while Swing paints.
|
||||
* frame that should have gone into the window but couldn't isn't tried again right away. A frame for a scene
|
||||
* replaced meanwhile is never shown.
|
||||
*/
|
||||
private void finishFrame(OrbitScene frameScene, OrbitCamera frameCamera, OrbitPicture drawn, boolean missedScreen,
|
||||
long requestedAtNanos) {
|
||||
private void finishFrame(OrbitScene frameScene, OrbitCamera frameCamera, ScreenTarget screen, OrbitPicture drawn,
|
||||
boolean missedScreen, long requestedAtNanos) {
|
||||
rendering = false;
|
||||
measureFrameTime(System.nanoTime() - requestedAtNanos);
|
||||
boolean shown = frameScene == scene;
|
||||
if (shown) {
|
||||
if (frameScene == scene) {
|
||||
takePicture(drawn, frameCamera);
|
||||
paintPicture(drawn);
|
||||
showInWindow(screen, drawn);
|
||||
}
|
||||
boolean canGoOnScreen = !missedScreen && canvas != null && !canvas.isVisible() && drawsOnScreen();
|
||||
if (renderAgain || drawn.catchingUp() || canGoOnScreen) {
|
||||
renderAgain = false;
|
||||
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.
|
||||
|
||||
Reference in new issue
Block a user