husker-dev / openglfx

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

AsyncSharedGL has blurred screen! #63

Open qq1053831109 opened 7 months ago

qq1053831109 commented 7 months ago

QQ图片20231129222909

qq1053831109 commented 7 months ago

it is 4.0.0 version.

husker-dev commented 7 months ago

Can you try to use at least 4.0.1 version? There were a lot of async canvas fixes.

Also, is the problem only with these artifacts, or are there other problems? Are you resizing these Canvas?

husker-dev commented 7 months ago

@qq1053831109 I can't help you without feedback

husker-dev commented 7 months ago

Check version 4.0.3, there are some more changes related to this.

qq1053831109 commented 7 months ago

the only happened on some laptops. The testing cycle is relatively long. It was submitted to me by my product user. My desktop computer won't have a splash screen. More feedback requires a longer cycle to obtain

qq1053831109 commented 7 months ago

I will be using 4.03 in the next version and it is expected to take a month to receive feedback on whether to fix it

qq1053831109 commented 7 months ago

the 4.05 It's hard to upgrade, it's hard to hook up. My usage is very complex and requires me to use my own context management method in different situations

qq1053831109 commented 7 months ago

More open public is needed, do not use internal. we need extends your class

husker-dev commented 7 months ago

If deep integration into the library is required, then it is better to simply clone it and change it to suit you. The library's internal mechanisms will not be exposed, as they may confuse other developers.

Also, if you decribe me your case, I may be able to help you to find the best solution.

qq1053831109 commented 7 months ago

package ose.core.sglfx.impl;

import com.badlogic.gdx.graphics.GL20;
import com.huskerdev.ojgl.GLContext;
import com.huskerdev.openglfx.GLExecutor;
import com.huskerdev.openglfx.canvas.GLProfile;
import com.huskerdev.openglfx.internal.fbo.Framebuffer;
import com.huskerdev.openglfx.internal.fbo.MultiSampledFramebuffer;
import com.sun.prism.Graphics;
import com.sun.prism.GraphicsPipeline;
import com.sun.prism.PixelFormat;
import com.sun.prism.Texture;
import ose.core.sglfx.GLThread;
import ose.core.sglfx.SGLContext;
import ose.core.sglfx.SolarGlFx;
import ose.core.sglfx.SolarOpenGLCanvas;
import ose.core.sglfx.addons.PassthroughShader;
import ose.core.sglfx.addons.SGlFxUtil;
import ose.core.sglfx.addons.Size;

import java.util.concurrent.atomic.AtomicBoolean;

import static com.huskerdev.openglfx.GLExecutor.glFinish;
import static com.huskerdev.openglfx.GLExecutor.glViewport;

public class SolarAsyncSharedImpl extends SolarOpenGLCanvas {
    private GLExecutor executor;

    {
        setMinSize(16, 16);
    }

    public SolarAsyncSharedImpl(GLExecutor executor) {
        super(true);
        this.executor = executor;
    }

    public SolarAsyncSharedImpl(GLExecutor executor, GLProfile profile, boolean flipY, int msaa) {
        super(true);
        this.executor = executor;
    }

    private Object paintLock = new Object();
    private Object blitLock = new Object();

    private Size lastDrawSize = new Size(-1, -1);
    private Size lastResultSize = new Size(-1, -1);

    private static SGLContext parallelContext = null;
    //    private static GLContext parallelContext = null;
    private static GLContext resultContext = null;
    private static GLContext fxContext = null;

    private Framebuffer resultFBO;
    private Framebuffer interThreadFBO;
    private Framebuffer fbo;
    private MultiSampledFramebuffer msaaFBO;

    private Texture fxTexture;

    private AtomicBoolean needsBlit = new AtomicBoolean(false);

    private PassthroughShader passthroughShader;

    private void initializeThread() {
        fxContext = GLContext.current();
        GLContext.clear();
//        parallelContext = GLContext.create(fxContext, true);
        parallelContext = SGLContext.create(fxContext.getHandle(), true);
        resultContext = GLContext.create(fxContext, true);
        fxContext.makeCurrent();

    }

    private void addUpdateCallBack() {
        GLThread.getGLThread(() -> {
            SGLContext.gdxContextThread = Thread.currentThread();
            parallelContext.makeCurrent();
            executor.initGLFunctions();
            SolarGlFx.solarGameApp.create();
        }).addUpdateCallBack(() -> {
            try {
                if (!getDisposed() && needRender()) {
                    if (System.currentTimeMillis() - lastRendedTime < limitedRenderTime) {
                        return;
                    }
                    lastRendedTime = System.currentTimeMillis();
                    paint();
                    synchronized (blitLock) {
                        fbo.blitTo(interThreadFBO.getId());
                    }
                    needsBlit.set(true);
//                    synchronized (paintLock) {
//                        paintLock.wait();
//                    }
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });
    }

    private void paint() {
        lastDrawSize.onDifference((int) getScaledWidth(), (int) getScaledHeight(), () -> {
            updateDrawFramebufferSize(getScaledWidth(), getScaledHeight());
            fireReshapeEvent(getScaledWidth(), getScaledHeight());
        });
        glViewport(0, 0, lastDrawSize.width, lastDrawSize.height);
        if (input != null) {
            input.update();
        }
        SolarGlFx.glFxApp.executedRunnables();
        if (getMsaa() == 0) {
            executor.glBindFramebuffer(GL20.GL_FRAMEBUFFER, fbo.getId());
            fireRenderEvent(fbo.getId());
        } else {
            executor.glBindFramebuffer(GL20.GL_FRAMEBUFFER, msaaFBO.getId());
            fireRenderEvent(msaaFBO.getId());
            msaaFBO.blitTo(fbo.getId());
        }
        glFinish();
    }

    private boolean _needAddUpdate2GLThread = true;

    protected void onNGRender(Graphics g) {
        if (getWidth() == 0 || getHeight() == 0)
            return;

        if (fxContext == null) {
            initializeThread();
        }
        if (_needAddUpdate2GLThread) {
            addUpdateCallBack();
            _needAddUpdate2GLThread = false;
        }
        needRenderTime = System.currentTimeMillis() + 1000;

        if (needsBlit.getAndSet(false)) {
            resultContext.makeCurrent();

            if (passthroughShader == null) {
                passthroughShader = new PassthroughShader();
            }

            lastResultSize.onDifference((int) getScaledWidth(), (int) getScaledHeight(), () -> {
                updateResultFramebufferSize(getScaledWidth(), getScaledHeight());
            });

            glViewport(0, 0, lastResultSize.width, lastResultSize.height);

            synchronized (blitLock) {
                passthroughShader.copy(interThreadFBO, resultFBO);
            }
            fxContext.makeCurrent();
        }

        if (fxTexture != null) {
            drawResultTexture(g, fxTexture);
        }

    }

    private void updateResultFramebufferSize(int width, int height) {
        if (resultFBO != null) {
            resultFBO.delete();
        }

        // Create JavaFX texture
        if (fxTexture != null) {
            fxTexture.dispose();
        }

        fxTexture = GraphicsPipeline.getDefaultResourceFactory()
                .createTexture(PixelFormat.BYTE_BGRA_PRE, Texture.Usage.DYNAMIC, Texture.WrapMode.CLAMP_TO_EDGE, width, height);
        fxTexture.makePermanent();

        // Create framebuffer that connected to JavaFX's texture
        resultFBO = new Framebuffer(width, height, SGlFxUtil.getGLTextureId(fxTexture), 0xDE1);
    }

    private void updateDrawFramebufferSize(int width, int height) {
        if (fbo != null) {
            fbo.delete();
            if (getMsaa() != 0) msaaFBO.delete();
        }

        // Create 'buffer' framebuffer
        interThreadFBO = new Framebuffer(width, height, -1, 0xDE1);

        // Create framebuffer
        fbo = new Framebuffer(width, height, -1, 0xDE1);
        fbo.bindFramebuffer();

        // Create multi-sampled framebuffer
        if (getMsaa() != 0) {
            msaaFBO = new MultiSampledFramebuffer(getMsaa(), width, height);
            msaaFBO.bindFramebuffer();
        }
    }

    @Override
    public int getFbo() {
        if (fbo == null) {
            return 0;
        }
        return fbo.getId();
    }

    public void repaint() {
        synchronized (paintLock) {
            paintLock.notifyAll();
        }
    }

    public void timerTick() {
        if (needsBlit != null && needsBlit.get())
            markDirty();
    }

    public void dispose() {
        super.dispose();
        synchronized (paintLock) {
            paintLock.notifyAll();
        }
//        GLContext.delete(parallelContext);
        GLContext.delete(resultContext);
    }
}```
qq1053831109 commented 7 months ago

like javafx all pane in one javafx Thread. gl need in one Thread。not every gl panel has independent thread. I have 100 gl panels at the same time.The underlying layers are all in one libgdx thread

husker-dev commented 7 months ago

Threads

For a large amount of async GLCanvas having an independent thread is not good. I can make some kind of shared thread that can be applied to several GLCanvas. This way you can divide a large number of GLCanvas into groups and reduce the number of threads.

image

About LibGDX.

Like regular OpenGL, it can run on different threads. I can't suggest anything without analyzing the application code. I made a module for LibGDX, but it hasn't been tested yet.