Problem with AWTGLCanvas

Started by AGP, August 26, 2025, 11:58:45 PM

Previous topic - Next topic

AGP

Egon, have you ever run into the problem with AWTGLCanvas in which the entire rendering is out of place on the canvas that I did about a decade ago and ever since on every computer I've ever run my stuff on? I'd love to have that fixed (most likely by switching to lwjgl 3 and convincing them to add AWTGLCanvas back). What do you think?

EgonOlsen

I've moved the question into a new topic, just in case you are wondering.

I'm not aware of a problem with the location of AWTGLCanvas but then again, I've never really used it in situations where it doesn't use the whole frame. So I never tried to use it as part of an actual UI.

Have to tried to disable Java2D's accelerated rendering pipeline? Maybe it's a problem with that (albeit I doubt it, but it might be worth a try).

AGP

I've tried everything. Test it out as an actual UI and let me know, please.

AGP

Although I should reiterate that I usually use it on the entire frame. I'm making a sometimes-2d sometimes 3d game and it would be available there.


EgonOlsen

I'm not of much help here, I'm afraid. I've actually no clue about UI stuff in Java. All I'm usually doing is to open a JFrame/Frame and then draw stuff all by myself.

I dimly remember that others tried to mix an actual UI with the AWTGLCanvas and IIRC, it didn't work our too well (but was possible to a degree). But I couldn't find this thread.

Have you actually tried this approach: https://www.jpct.net/wiki/index226d.html?title=Using_jPCT_in_Swing

AGP

I usually use AWT, but I'll try Swing to see if it's still there. Have you used the Canvas on a AWT frame to see the problem?

AGP

This seems to be software. Am I missing something?

EgonOlsen

Quote from: AGP on September 26, 2025, 01:47:01 AMThis seems to be software. Am I missing something?
I'm not sure what you mean!?

AGP

I tried quoting this line, but it didn't show up for some reason:
Have you actually tried this approach: https://www.jpct.net/wiki/index226d.html?title=Using_jPCT_in_Swing

This example seems to be using software rendering.

AGP

#10
CG proposed the following changes to AWTJPCTCanvas. If you could possibly compile it for me (I've never compiled jpct, only my C# port) so that I could test it I would be very grateful:

package com.threed.jpct;

import org.lwjgl.opengl.*;

import java.awt.GraphicsConfiguration;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.geom.AffineTransform;

class AWTJPCTCanvas extends AWTGLCanvas implements IThreadBuffer {

      private static final long serialVersionUID = 1L;

      private AWTGLRenderer renderer = null;

      private AWTDisplayList[] list = null;

      private AWTDisplayList onceList = null;

      private int curList = 0;

      private Object lock = new Object();

      private boolean paintObserver = false;

      private Object[] result = null;

      AWTJPCTCanvas() throws Exception {
      }

      AWTJPCTCanvas(AWTGLRenderer myRenderer) throws Exception {
              super();
              init(myRenderer);
      }

      AWTJPCTCanvas(AWTGLRenderer myRenderer, PixelFormat pf) throws Exception {
              super(pf);
              init(myRenderer);
      }

      public void setSamples(int samples) {
              // Bereits im PF erledigt...hier egal...
      }

      protected void init(AWTGLRenderer myRenderer) {
              renderer = myRenderer;
              list = new AWTDisplayList[2];
              list
[o]= new AWTDisplayList();
              list[1] = new AWTDisplayList();
              onceList = new AWTDisplayList();

              // Reassert viewport after resizes so device-pixel size stays in sync.
              addComponentListener(new ComponentAdapter() {
                    @Override
                    public void componentResized(ComponentEvent e) {
                            // Trigger a repaint so paintGL() can recompute the viewport.
                            repaint();
                    }
              });
      }

      public void exceptionOccurred(org.lwjgl.LWJGLException e) {
              if (e.getMessage().indexOf("No support for") != -1) {
                    // A hack to take care of missing aa-support in intel. Injects a new
                    // pixel format into the canvas vie reflection and makes the
                    // canvas think that it's new...
                    try {
                            java.lang.reflect.Field field = AWTGLCanvas.class.getDeclaredField("pixel_format");
                            field.setAccessible(true);
                            PixelFormat pf = (PixelFormat) field.get(this);
                            if (pf == null || pf.getSamples() != 0) {
                                  Logger.log("No support for multi sampling...trying to recover!", Logger.WARNING);
                                  field.set(this, new PixelFormat());

                                  java.lang.reflect.Field count = AWTGLCanvas.class.getDeclaredField("reentry_count");
                                  java.lang.reflect.Field peer = AWTGLCanvas.class.getDeclaredField("peer_info");
                                  count.setAccessible(true);
                                  peer.setAccessible(true);
                                  peer.set(this, (Object[]) null);
                            } else {
                                  Logger.log(e, Logger.ERROR);
                            }
                    } catch (Throwable e1) {
                            Logger.log(e1, Logger.WARNING);
                            e.printStackTrace();
                            Logger.log(e, Logger.ERROR);
                    }
              } else {
                    Logger.log(e, Logger.ERROR);
              }

      }

      public void dispose() {
              setEnabled(false);
      }

      public final void add(int command, Object params) {
              list[curList].add(command, params);
      }

      public void add(Camera proj, int[] ambient) {
              Object[] params = new Object[2];
              Camera cam2 = new Camera();
              cam2.setBack(proj.getBack().cloneMatrix());
              cam2.setPosition(proj.getPosition());
              cam2.setFOVLimits(proj.getMinFOV(), proj.getMaxFOV());
              cam2.setFOV(proj.getFOV());
              params
[o]= cam2;

              int[] am = new int[ambient.length];
              for (int i = 0; i < am.length; i++) {
                    am[i] = ambient[i];
              }

              params[1] = am;
              add(GLBase.MODE_SET_CAMERA, params);
      }

      public final void addOnce(int command, Object params) {
              onceList.addOnce(command, params);
      }

      public Object getLock() {
              return lock;
      }

      public final void add(VisList v, int s, int e, int m) {
              list[curList].add(v, s, e, m);
      }

      public void enableRenderTarget() {
              list[curList].enableRenderTarget();
      }

      public void disableRenderTarget() {
              list[curList].disableRenderTarget();
      }

      public boolean hasRenderTarget() {
              return list[curList].hasRenderTarget();
      }

      public final void setColor(int col) {
              list[curList].setColor(col);
      }

      public final AWTDisplayList getDisplayList() {
              return list[curList];
      }

      /*
        * public final void fillInstances() { synchronized (lock) {
        * list[curList].fillInstances(); } }
        */
      public final void switchList() {
              if (Config.synchronizedRendering) {
                    while (!list[curList ^ 1].rendered) {
                            Thread.yield();
                    }
              }
             
              synchronized (lock) {
                    list[curList].switchBuffers();
                    curList ^= 1;
                    list[curList].reset();
              }
      }

      public final boolean hasBeenPainted() {
              return paintObserver;
      }

      public final void observePainting() {
              paintObserver = false;
      }

      public final Object[] getPaintResults() {
              if (hasBeenPainted()) {
                    Object[] res = result;
                    result = null;
                    return res;
              }
              return null;
      }

      // --- HiDPI fix: ensure the GL viewport matches the device-pixel backbuffer ---
      /** Ensure GL viewport matches device-pixel backbuffer (fixes HiDPI offset). */
      private void ensureHiDPIVP() {
              int fbW, fbH;
              GraphicsConfiguration gc = getGraphicsConfiguration();
              if (gc != null) {
                    AffineTransform tx = gc.getDefaultTransform();
                    double sx = tx.getScaleX(), sy = tx.getScaleY();
                    int lw = Math.max(1, getWidth());
                    int lh = Math.max(1, getHeight());
                    fbW = (int)Math.round(lw * sx);
                    fbH = (int)Math.round(lh * sy);
              } else {
                    fbW = Math.max(1, getWidth());
                    fbH = Math.max(1, getHeight());
              }
              GL11.glViewport(0, 0, fbW, fbH);
      }
      // ------------------------------------------------------------------------------

      public void paintGL() {

              try {
                    makeCurrent();

                    // HiDPI: align viewport to device-pixel framebuffer before any rendering.
                    ensureHiDPIVP();

                    if (!renderer.isDisposed()) {
                            if (!renderer.isInitialized()) {
                                  renderer.init();
                            }
                            renderer.execute(GLBase.MODE_START_PAINTING, null);

                            synchronized (lock) {
                                  // Normales Zeichnen...
                                  int ind = curList ^ 1;
                                  AWTDisplayList al = list[ind];

                                  int end = al.count;
                                  for (int i = 0; i < end; i++) {
                                          int mode = al.mode[i];
                                          switch (mode) {
                                          case (AWTDisplayList.EXECUTE): {
                                                renderer.executeGL(al, i);
                                                break;
                                          }
                                          case (AWTDisplayList.ARRAY): {
                                                renderer.drawVertexArray(al, i);
                                                break;
                                          }
                                          case (AWTDisplayList.STRIP): {
                                                renderer.drawStrip(al, i);
                                                break;
                                          }
                                          case (AWTDisplayList.POLYGON): {
                                                renderer.drawPolygon(al, i);
                                                break;
                                          }
                                          case (AWTDisplayList.WIREFRAME): {
                                                renderer.drawWireframe(al, i);
                                                break;
                                          }
                                          }
                                  }

                                  // "Once"-Befehle ausführen (pixelgrabben und Context
                                  // befragen z.B.)!
                                  boolean po = paintObserver;
                                  if (onceList.count != 0) {
                                          boolean pop = false;
                                          for (int i = 0; i < onceList.count; i++) {
                                                int mode = onceList.mode[i];
                                                if (mode == AWTDisplayList.EXECUTE_ONCE && !po) {
                                                        result = renderer.executeGL(onceList, i);
                                                        onceList.delete(i);
                                                        pop = true;
                                                }
                                          }

                                          paintObserver = pop;
                                  }
                                  al.rendered = true;
                            }
                    }
                    renderer.execute(GLBase.MODE_END_PAINTING, null);
                    swapBuffers();
              } catch (Exception e) {
                    throw new RuntimeException(e);
              }
      }

      public boolean isVisible() {
              return super.isVisible();
      }
}


EgonOlsen

Quote from: AGP on September 27, 2025, 09:01:49 AMI tried quoting this line, but it didn't show up for some reason:
Have you actually tried this approach: https://www.jpct.net/wiki/index226d.html?title=Using_jPCT_in_Swing

This example seems to be using software rendering.

I don't think so. At the least the text says it's for the AWTGL-Renderer. As mentioned, I've never tried anything like that myself.

AGP

#12
It's software. I managed to recompile just the one class. And the fix worked!

EgonOlsen

I've compiled a new version that includes that change (actually, the code above isn't 100% correct, because it does some changes to the existing code that make no sense, but I've omitted those).

Can you try this one and see if it also fixes your issue: https://www.jpct.de/download/net/jpctapi_133.zip

AGP

Thanks very much. I'll let you know before I sleep.