husker-dev / openglfx

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

RenderDoc support #30

Open dmitrykolesnikovich opened 1 year ago

dmitrykolesnikovich commented 1 year ago

GL context is created on Windows platform like so https://github.com/husker-dev/offscreen-jgl/blob/main/native/windows.cpp#L137 and optically context_attributes is here. But RenderDoc says "Context is created not via createContextAttribs" (see screenshot). I never worked closely with native stuff myself. Maybe you can help me to make RenderDoc happy?

image

husker-dev commented 1 year ago

I think this is because I don't specify the version when creating the context, so the opengl version is below 3.2.

Try to print the version of the current context with the following lines:

glGetInteger(GL_MAJOR_VERSION)
glGetInteger(GL_MINOR_VERSION)

Also, try to change OpenGL profile to core (or compatibility)

dmitrykolesnikovich commented 1 year ago

I added printing versions inside LWJGLExecutor.initGLFunctions like so

println("major version: ${GL11.glGetInteger(GL30.GL_MAJOR_VERSION)}")
println("minor version: ${GL11.glGetInteger(GL30.GL_MINOR_VERSION)}")

and it printed me

major version: 4
minor version: 6
dmitrykolesnikovich commented 1 year ago

When I create 4.6 gl context like this

JNIEXPORT jlongArray JNICALL Java_com_huskerdev_ojgl_platforms_WinGLPlatform_nCreateContext(JNIEnv* env, jobject, jboolean isCore, jlong shareRc) {
    checkBasicFunctions();

    GLint context_attributes[] = {
            WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
            WGL_CONTEXT_MINOR_VERSION_ARB, 6,
            WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
            0
    };

    HGLRC rc;
    if (!(rc = wglCreateContextAttribsARB(dc, (HGLRC)shareRc, context_attributes)))
    std::cout << "Failed to create context (WGL)" << std::endl;

    jlong array[2] = { (jlong)rc, (jlong)dc };
    return createLongArray(env, 2, array);
}

then creation of texture does not work anymore (returns null because this returns false). I believe solving this should help with RenderDoc eventually because other people fixed their stuff this way.

dmitrykolesnikovich commented 1 year ago

Funny thing if you comment out explicit declaration of GL version in context_attributes then everything will work properly as expected (BTW does not matter what GL version is explicitly requested 3.3, 4.6, etc)

JNIEXPORT jlongArray JNICALL Java_com_huskerdev_ojgl_platforms_WinGLPlatform_nCreateContext(JNIEnv* env, jobject, jboolean isCore, jlong shareRc) {
    checkBasicFunctions();

    GLint context_attributes[] = {
            // WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
            // WGL_CONTEXT_MINOR_VERSION_ARB, 6,
            WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
            0
    };

    HGLRC rc;
    if (!(rc = wglCreateContextAttribsARB(dc, (HGLRC)shareRc, context_attributes)))
    std::cout << "Failed to create context (WGL)" << std::endl;

    jlong array[2] = { (jlong)rc, (jlong)dc };
    return createLongArray(env, 2, array);
}
dmitrykolesnikovich commented 1 year ago

So I created branch with detailed description of how to reproduce RenderDoc issue on any machine with RenderDoc installed. Maybe this will motivate you to try it yourself. (JDK 18 needed)

husker-dev commented 1 year ago

Sorry, but I won't be able to test anything as I only have a phone right now. And it will be like this for another 4 months.

JavaFX creates empty texture with core profile in your case because of the different OpenGL context versions. Try to set d3d pipeline at the beginning of JavaFX app.

dmitrykolesnikovich commented 1 year ago

thanks for the suggestion, hope you are doing good

yetyman commented 1 year ago

Hello,

I am also attempting to get Renderdoc to attach to my openglfx application. I see you've noted a fix for this issue above

Funny thing if you comment out explicit declaration of GL version in context_attributes then everything will work properly as expected (BTW does not matter what GL version is explicitly requested 3.3, 4.6, etc)

JNIEXPORT jlongArray JNICALL Java_com_huskerdev_ojgl_platforms_WinGLPlatform_nCreateContext(JNIEnv* env, jobject, jboolean isCore, jlong shareRc) {
    checkBasicFunctions();

    GLint context_attributes[] = {
            // WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
            // WGL_CONTEXT_MINOR_VERSION_ARB, 6,
            WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
            0
    };

    HGLRC rc;
    if (!(rc = wglCreateContextAttribsARB(dc, (HGLRC)shareRc, context_attributes)))
    std::cout << "Failed to create context (WGL)" << std::endl;

    jlong array[2] = { (jlong)rc, (jlong)dc };
    return createLongArray(env, 2, array);
}

but I do not understand how someone would apply that fix. It looks like c++ which I understand, but it seems as though you used this code and saw its effect. How could I go about using this as well? or how can i get a version of this codebase with that fix applied?

I see the .h files for win_utils.h and wgl.h and I see the line

@JvmStatic private external fun nCreateContext(isCore: Boolean, shareWith: Long): LongArray in WinGLPlatform.kt

I can see that it is calling into

win_utils_x64.dll so i suppose i need to add this function to win_utils.h then rebuild the dll. Which i did, but i fell short at linking it correctly into the WinUtils.kt functions

I am unclear on a few things. Perhaps due to my lack of familiarity with Kotlin and jni.

would you be willing to share the version of this code that includes this workaround for renderdoc?

husker-dev commented 1 year ago

As I wrote earlier, now I have nothing better than just a smartphone. So I can make any changes in the code only in the summer

dmitrykolesnikovich commented 1 year ago

I literally rebuild native ptoject https://github.com/husker-dev/offscreen-jgl

And then replaced .dll file with new one here https://github.com/husker-dev/openglfx/tree/master/core/src/main/resources/com/huskerdev/openglfx/natives

dmitrykolesnikovich commented 1 year ago

Kotlin approach to jni is simple external keyword that looks for implementation inside .dll packed inside JAR as resource.

yetyman commented 1 year ago

Thank you @dmitrykolesnikovich !

yetyman commented 1 year ago

Funny thing if you comment out explicit declaration of GL version in context_attributes then everything will work properly as expected (BTW does not matter what GL version is explicitly requested 3.3, 4.6, etc)

@dmitrykolesnikovich when you said this, did you mean that renderdoc could attach and capture? That is what I took it to mean, but using the dlls you've referenced above I am still getting the context attributes error when the fat jar from your branch project is run through RenderDoc.

what purpose would rebuilding the ojgl project serve? it appears that its nCreateContext function already does not include the wgl version numbers in version 1.2's files. https://github.com/husker-dev/offscreen-jgl/blob/main/native/windows.cpp Am I perhaps looking at the wrong code?

dmitrykolesnikovich commented 1 year ago

Well there is no working solution at the moment, the purpose is to play around with native code to find not only that it does not work, but also that there are different ways how it does not work. At least something to generate ideas on how to actually make it to work.

Tldr. No reason to rebuild ojgl

yetyman commented 1 year ago

Gotcha, I just misinterpreted. I will let you know if I figure anything out. For now I'm just reimplementing my project in C# with OpenTK to get the GFX debugging I need. Incredibly suboptimal, but I'm already familiar with the OpenTK abstraction.

dmitrykolesnikovich commented 1 year ago

You can try to replace GL2 with GL3 here https://github.com/husker-dev/openglfx/blob/master/jogl/src/main/kotlin/com/huskerdev/openglfx/jogl/JOGLFXCanvas.kt#L16

This is not based on any understanding, just yet another thing to try

var gl: GL3? = null
        get() {
            if(field == null)
                field = GLDrawableFactoryImpl.getFactoryImpl(GLProfile.get(GLProfile.GL3)).createExternalGLContext().gl.gL3
            return field
        }
husker-dev commented 1 year ago

It seems to me that RenderDoc is trying to attach to OpenGL context created by JavaFX. It uses "old fashioned" way that doesn't include any version and type (core/compatibility) selection. So, your problem occurs. It looks like this:

// Window creation
hwnd = createDummyWindow(szAppName);
hdc = GetDC(hwnd);

// Getting context using window
hglrc = wglCreateContext(hdc);

But OpenGLFX uses its own context with a "new" creation way. It includes wglCreateContextAttribsARB and other brand new OpenGL features.


So, I think the problem is just incorrect selected context in RenderDoc.

husker-dev commented 10 months ago

Since OpenGLFX creates an offscreen GL contexts to perform rendering, RenderDoc needs to get some signals to fetch this context. It can be done using this API https://renderdoc.org/docs/in_application_api.html

I will try to implement it in near future.

dmitrykolesnikovich commented 10 months ago

That will be very-very relevant for my work. I am not a c++ guy, so will rely on your solution as is.

husker-dev commented 10 months ago

Some updates

Well, I've combined RenderDoc and OpenGLFX, but the examples doesn't shows up as they use old-fashioned OpenGL. There are also some issues with wgl_nv_dx_interop which is used with the D3D pipeline on Windows.

Whatever, it is already something...

image

dmitrykolesnikovich commented 10 months ago

Actually in my first post I have opengl connection established (see picture). I am not sure wheter this is relevant though :)

husker-dev commented 10 months ago

You have connected to JavaFX but not to GL instance from OpenGLFX

dmitrykolesnikovich commented 10 months ago

Understand.

husker-dev commented 10 months ago

After some testing, I think it would be better not to use the RenderDoc API, but just be able to output the rendering simultaneously to another window that RenderDoc can connect to.

For now I'm only implementing this for Windows as it's the easiest thing to do.

husker-dev commented 9 months ago

I solved problems in the RenderDoc implementation. Now I will close the issue as completed.

A new version will be released soon, may be in a few days, when I test it with Linux and MacOS.


If you think that the problem is not resolved, reopen this issue.

dmitrykolesnikovich commented 5 months ago

Sorry my ignorance but it's still None in the API image

dmitrykolesnikovich commented 5 months ago

I built fat jar and than ran it from inside Renderdoc via java -jar example.jar

husker-dev commented 5 months ago

There is a combobox with a selection of subprocesses. One of them is a process with OpenGL

dmitrykolesnikovich commented 5 months ago

I do attach to the process but without API being recognized. image

husker-dev commented 5 months ago

I can check in a week. I don't have access to my Windows device right now.

dmitrykolesnikovich commented 5 months ago

My simple setup to reproduce:

1) build fat jar ES-4.0.5.jar via gradlew modules:lwjgl2:fatJarES with brand new fatJarES gradle task

task fatJarES(type: Jar) {
    manifest {
        attributes 'Main-Class': 'LWJGL2_ES2Kt'
    }
    archiveBaseName = 'ES'
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
    from { sourceSets.examples.runtimeClasspath.filter { file -> file.exists() }.collect { it.isDirectory() ? it : zipTree(it) } }
    with jar
}

image

2) run lwjgl2.bat from renderdoc

123123

image

yetyman commented 4 weeks ago

I solved problems in the RenderDoc implementation. Now I will close the issue as completed.

A new version will be released soon, may be in a few days, when I test it with Linux and MacOS.

If you think that the problem is not resolved, reopen this issue.

What was the workaround? Do I just call RenderDoc.bind(canvas, KeyCode.F12); as the Readme says when I create the canvas? Does RenderDoc still need to be bound to the process before this happens?

husker-dev commented 4 weeks ago

RenderDoc.bind(canvas) doesn't work for some reason. I'll leave that until the next big release. For now you can use RenderDoc.startFrameCapture() and RenderDoc.startFrameCapture() for segments that you want to capture.

yetyman commented 4 weeks ago

gotcha. That explains a little bit.

I am having some trouble using the render doc methods.

When I use the bind method I get the error about NVDXInterop and so instead I create my canvas with InteropType.Blit, but when I do this the glGetInteger(GL_FRAMEBUFFER_BINDING) returns 0 instead of the 2 I typically see it start at. I grab your built in FBO id for adding color attachments in the way you helped me with here

Is it the case that changing the InteropType to Blit means that OpenGLFX no longer uses a FrameBuffer for the render? I would have though that even if it uses a blit operation to transfer the image that it would still render in the same way onto a framebuffer...

But if the Bind command is broken anyways that's not my issue. Do I still need to use InteropType.Blit with the RenderDoc.StartFrameCapture method?

Without using InteropType.Blit... I have attempted to inject RenderDoc before creating my canvas and I see

image

then i let my application continue and for a moment RenderDoc shows me

image

then the JVM crashes hs_err_pid52152.log

siginfo: EXCEPTION_ACCESS_VIOLATION (0xc0000005), reading address 0x0000000000000000

Current thread (0x00000200c589f9c0):  JavaThread "QuantumRenderer-0" daemon [_thread_in_native, id=97872, stack(0x0000009e42000000,0x0000009e42100000) (1024K)]

Stack: [0x0000009e42000000,0x0000009e42100000],  sp=0x0000009e420fdcd0,  free space=1015k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C  [renderdoc.dll+0x9e9e16]

Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j  com.huskerdev.openglfx.internal.d3d9.NVDXInterop.wglDXLockObjectsNV(JJ)Z+0
j  com.huskerdev.openglfx.internal.d3d9.NVDXInterop.access$wglDXLockObjectsNV(JJ)Z+2
j  com.huskerdev.openglfx.internal.d3d9.NVDXInterop$Companion.wglDXLockObjectsNV(JJ)Z+2
j  com.huskerdev.openglfx.internal.d3d9.NVDXInterop$Companion.access$wglDXLockObjectsNV(Lcom/huskerdev/openglfx/internal/d3d9/NVDXInterop$Companion;JJ)Z+3
j  com.huskerdev.openglfx.internal.d3d9.NVDXInterop$NVDXObject.lock()Z+14
j  com.huskerdev.openglfx.internal.canvas.NVDXInteropCanvasImpl.renderContent(Lcom/sun/prism/Graphics;)V+248
j  com.sun.javafx.sg.prism.NGNode.doRender(Lcom/sun/prism/Graphics;)V+330
j  com.sun.javafx.sg.prism.NGNode.render(Lcom/sun/prism/Graphics;)V+35
j  com.sun.javafx.sg.prism.NGGroup.renderContent(Lcom/sun/prism/Graphics;)V+151
j  com.sun.javafx.sg.prism.NGRegion.renderContent(Lcom/sun/prism/Graphics;)V+111
...
husker-dev commented 4 weeks ago

@yetyman


The reason why InteropType.Blit returns 0 framebuffer is because the glblitframebuffer function is used to transfer the image from opengl to javafx, which does not require additional buffers. And again, 0 is normal - it is the index of the very first buffer in the opengl life cycle.

Without InteropType.Blit, NVDXInterop is used, which requires the creation of a some “transit” framebuffers to send the image. Openglfx does this, and gives you some other buffer to use.

You don't have to worry about the framebuffer index that openglfx generates.


For some reason renderdoc doesn't want to work with NVDXInterop. I do not know why. This is the reason of crash

yetyman commented 3 weeks ago

With InteropType.Blit on, I get an error binding the render buffer to Color_Attachment_1: Invalid Operation(1282) My command is glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_RENDERBUFFER, myRenderBufferID);

researching... https://registry.khronos.org/OpenGL-Refpages/es2.0/xhtml/glFramebufferRenderbuffer.xml it looks like color attachments can't be added to the default frame buffer, so in order to debug i think i will need to make my own intermediate framebuffer to render onto then copy its data to the default frame buffer

yetyman commented 3 weeks ago

I'm using my own FrameBuffer now and its working! I can finally use RenderDoc! Thank you