husker-dev / openglfx

OpenGL implementation for JavaFX
Apache License 2.0
80 stars 10 forks source link

Animator frequency not honored #74

Open jodysowald opened 6 months ago

jodysowald commented 6 months ago

More render calls than necessary I am creating a GLCanvas with LWJGL and adding an animator with 30.0 as the FPS, but I am seeing no limit on draw calls. Is this intentional? I would expect no more than 30 render calls per second in this scenario but i am seeing upwards of 90 calls per second in my render code.

To Reproduce

GLCanvas canvas = new GLCanvas(LWJGL_MODULE, GLProfile.Compatibility, true, msaa, false);
canvas.setAnimator(new GLCanvasAnimator(30.0));

Environment:

husker-dev commented 6 months ago

The canvas can also be updated when the size or visibility changes. Is any of this used?

jodysowald commented 6 months ago

The canvas is not currently resizing nor is its visibility changing. If it is relevant, I do have overlapping transparent boxes in the javafx application

image
jodysowald commented 6 months ago

hold on, more information incoming

jodysowald commented 6 months ago

Nevermind, apparently a resize is being invoked though the width and height are not changing... I guess i need to debug the JFX layout. That's my bad. Thank you.

husker-dev commented 6 months ago

Why is fixed fps so important? If it is used for movement, then it is better to use delta time from the render event.

jodysowald commented 6 months ago

I am optimizing resource usage so 90 FPS draw calls to the gpu would be troublesome. I may invent a little rate limit in the render code like


if(event.delta < (1/fps)) return;
jodysowald commented 6 months ago

btw. Resize on the JavaFX node is being invoked, but the reshape event is not as width and height are not actually changing

jodysowald commented 6 months ago
getChildren().add(canvas);

canvas.setManaged(true);
canvas.setLayoutX(0);
canvas.setLayoutY(0);
canvas.minHeightProperty().bind(heightProperty());
canvas.prefHeightProperty().bind(heightProperty());
canvas.maxHeightProperty().bind(heightProperty());
canvas.minWidthProperty().bind(widthProperty());
canvas.prefWidthProperty().bind(widthProperty());
canvas.maxWidthProperty().bind(widthProperty());
canvas.minHeightProperty().addListener((a,b,c)->{
    canvas.minHeightProperty().get();
    log.info("canvas resized");
});

This is still causing many extra resize calls to my render function. This "canvas resized" log is not being triggered. How does openGLFX sense resizes such that it is still attempting to render extra frame even though it is managed and thus should not change dimensions outside of these bindings which are not currently invalidating.

husker-dev commented 6 months ago

openglfx behaves just like any other element in javafx - it has a "dirty" state, which says it needs to be redrawn. It is set when the external parameters of the element are changed (size, visibility, etc.). Along with canvas rendering, opengl rendering is also called.

I plan to do a separate type of rendering without unnecessary draws, but that will come later.

jodysowald commented 6 months ago

Thank you! should i expect any internal issues using this style of workaround for now?


long lastDrawNano = System.nanoTime();
public void render(GLRenderEvent event) {
    //simple rate limiting on render.
    // resize events where WxH do not change so useless extra render calls are being invoked
    if(System.nanoTime() < lastDrawNano + Duration.ofMillis((long) (1000/30.0)).toNanos())
        return;
    lastDrawNano = System.nanoTime();
    ...
husker-dev commented 6 months ago

Yes, you may get a blank screen when canvas was resized (fbo is recreating at this time) and you skipped the frame.

As a workaround for your workaround (lol) you can create a "double buffer" logic. Then you can print the second buffer instead of just skipping the frame.

husker-dev commented 6 months ago

or just don't let skipping when resizing was previously called