phoenicis icon indicating copy to clipboard operation
phoenicis copied to clipboard

Fetch graphics card info and make it available to script

Open ImperatorS79 opened this issue 6 years ago • 33 comments

It should be possible, for example, to at least fetch the graphic card manufacturer (intel/nvdia/amd) in order, for example, to set VK_ICD_FILENAME correctly for Vulkan application.

Fetching OpenGL core context version and Vulkan version could also be interesting, simply to test if drivers are installed (in 32 bits and 64 bits).

ImperatorS79 avatar Aug 21 '19 11:08 ImperatorS79

This is somewhat related to #1854.

plata avatar Aug 21 '19 16:08 plata

#1854 is more general, but yes. I see three ways of doing it:

  • Use LWJGL to get the rendering infos (advantage: pure java)
  • Use glxinfo and vulkaninfo (and call the program from Java) (advantage: nearly nothing to do)
  • Compile a simple C++ test program to check this (only advantage: can be compiled in 32 bits and 64 bits to check if the user have installed OpenGL/Vulkan 32 bits drivers)

I can do the GL or Vulkan calls quite easily ^^.

ImperatorS79 avatar Aug 21 '19 16:08 ImperatorS79

I have normally a working LWJGL code, which compiles and execute in js, but I do not know how to check the returned value. Current code (most part are simply copy paste from LWJGL tutorial ^^):

package org.phoenicis.tools.system;

import org.phoenicis.configuration.security.Safe;

import org.lwjgl.*;
import org.lwjgl.glfw.*;
import org.lwjgl.opengl.*;
import org.lwjgl.vulkan.*;

import static org.lwjgl.glfw.Callbacks.*;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.system.MemoryUtil.*;
import static org.lwjgl.glfw.GLFWVulkan.*;

@Safe
class GraphicsProperties {
    public GraphicsProperties() {
        Vendor = "Unknown";
        OpenGLVersion = "Unsupported";
        VulkanVersion = "Unsupported";
    }
	
    public String toString() {
        return Vendor + " " + OpenGLVersion + " " + VulkanVersion;
    }
	
    public String getVendor() {
        return this.Vendor;
    }
	
    public String getOpenGLVersion() {
        return this.OpenGLVersion;
    }
	
    public String getVulkan() {
        return this.VulkanVersion;
    }

    public String Vendor;
    public String OpenGLVersion;
    public String VulkanVersion;
}

@Safe
// Code from LWJGL tutorial
public class GraphicsPropertiesFetcher {
    private long window;

    private void init() {
        // Setup an error callback. The default implementation
        // will print the error message in System.err.
        GLFWErrorCallback.createPrint(System.err).set();

        // Initialize GLFW. Most GLFW functions will not work before doing this.
        if (!glfwInit())
            throw new IllegalStateException("Unable to initialize GLFW");

        // Configure GLFW
        glfwDefaultWindowHints(); // optional, the current window hints are already the default
        glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // the window will stay hidden after creation
        //glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); does not work if opengl <3.2

        // Create the window
        window = glfwCreateWindow(300, 300, "Test Window", NULL, NULL);
        if (window == NULL)
            throw new RuntimeException("Failed to create the GLFW window for testing graphic card capabilities");
    }

    private void terminate() {
        glfwFreeCallbacks(window);
        glfwDestroyWindow(window);

        glfwTerminate();
        glfwSetErrorCallback(null).free();
    }

    private void FetchVendorOpenGLVersion(GraphicsProperties graphicsProperties) {
        // Make the OpenGL context current
        glfwMakeContextCurrent(window);

        GL.createCapabilities();

        graphicsProperties.Vendor = glGetString(GL_VENDOR);
        graphicsProperties.OpenGLVersion = glGetString(GL_VERSION);
        graphicsProperties.OpenGLVersion = graphicsProperties.OpenGLVersion.substring(0, graphicsProperties.OpenGLVersion.indexOf(' '));

        GL.destroy();
    }

    private void FetchVulkanVersion(GraphicsProperties graphicsProperties) {
        if (!glfwVulkanSupported()) {
            return;
        }
        
        int version = VK.getInstanceVersionSupported();

        graphicsProperties.VulkanVersion = String.valueOf(version >> 22) + "." + String.valueOf((version >> 12) & 0x3ff) + "." + String.valueOf(version & 0xfff);
    }

    public GraphicsProperties GetProperties() {
        GraphicsProperties graphicsProperties = new GraphicsProperties();

        init();

        FetchVendorOpenGLVersion(graphicsProperties);
        FetchVulkanVersion(graphicsProperties);

        terminate();

        System.out.println(graphicsProperties.Vendor);
        System.out.println(graphicsProperties.OpenGLVersion);
        System.out.println(graphicsProperties.VulkanVersion);

        return graphicsProperties;
    }
}

ImperatorS79 avatar Aug 22 '19 12:08 ImperatorS79

but I do not know how to check the returned value.

What do you mean? To make GraphicsPropertiesFetcher accessible from the scripts you only need to add a

    @Bean
    public GraphicsPropertiesFetcher graphicsPropertiesFetcher() {
        return new GraphicsPropertiesFetcher();
    }

method to ToolsConfiguration.

Afterwards you can access the GraphicsPropertiesFetcher in your scripts via:

const graphicsProperties = Bean("graphicsPropertiesFetcher").GetProperties();

madoar avatar Aug 22 '19 16:08 madoar

And how do I access the content of the structure/class returned ? I tried graphicsProperties.Vendor, but it returned undefined.

ImperatorS79 avatar Aug 22 '19 16:08 ImperatorS79

That is exactly how you should do it. If you are unsure if the Java class works as you expect you can also add some debugging code in it, e.g. some System.out.println(....); statements. The most likely scenario is that glGetString(GL_VENDOR) returns null which may be interpreted as undefined by JS.

madoar avatar Aug 22 '19 16:08 madoar

I have the error "no bean 'graphicsPropertiesFetcher' available" :thinking:.

ImperatorS79 avatar Aug 22 '19 17:08 ImperatorS79

Did you add the above mentioned method to ToolsConfiguration and recompile Phoenicis afterwards?

madoar avatar Aug 22 '19 17:08 madoar

Yes, I did. Maybe I have done a spelling error somewhere because it worked some hours ago.

ImperatorS79 avatar Aug 22 '19 17:08 ImperatorS79

Okay, so the Vendor and GL Version are :

VMware, Inc.
3.1 Mesa 18.2.8

However, in .js I have undefined for the vendor (did not test GL version)

I will also have to do some work on the version to have it in the format 310 (which I will do in js when it works).

ImperatorS79 avatar Aug 22 '19 17:08 ImperatorS79

You can try adding a toString method to GraphicsProperties to check whether the object contains the right information. The method would then look like this:

public String toString() {
   return Vendor + " " + OpenGLVersion + " " + VulkanVersion;
}

Afterwards you can test the output via:

Java

System.out.println(graphicsProperties.toString());

JavaScript

print(Bean("graphicsPropertiesFetcher").GetProperties().toString());

madoar avatar Aug 22 '19 18:08 madoar

Seems it works with that method but not by accessing class member directly.

ImperatorS79 avatar Aug 23 '19 11:08 ImperatorS79

Try adding corresponding getter methods to the class to see if this helps, e.g.:

public String getVendor() {
   return this.Vendor;
}

madoar avatar Aug 23 '19 20:08 madoar

I get

[ERROR] org.phoenicis.multithreading.ControlledThreadPoolExecutorService (l.64) - TypeError: undefined is not a function
        at <js> :anonymous(Unnamed:18:698-727)
        at <js> go(Unnamed:81:3209-3243)
        at org.graalvm.truffle/com.oracle.truffle.polyglot.ObjectProxyHandler.invoke(HostInteropReflect.java:678)
        at com.sun.proxy.$Proxy43.go(Unknown Source)
        at org.phoenicis.javafx.components.application.skin.ApplicationInformationPanelSkin.lambda$installScript$7(ApplicationInformationPanelSkin.java:237)
        at org.phoenicis.scripts.session.PhoenicisInteractiveScriptSession.eval(PhoenicisInteractiveScriptSession.java:35)
        at org.phoenicis.scripts.interpreter.BackgroundScriptInterpreter.lambda$createInteractiveSession$1(BackgroundScriptInterpreter.java:45)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
        at java.base/java.lang.Thread.run(Thread.java:835)

Exception in thread "pool-3-thread-4" TypeError: undefined is not a function
        at <js> :anonymous(Unnamed:18:698-727)
        at <js> go(Unnamed:81:3209-3243)
        at org.graalvm.truffle/com.oracle.truffle.polyglot.ObjectProxyHandler.invoke(HostInteropReflect.java:678)
        at com.sun.proxy.$Proxy43.go(Unknown Source)
        at org.phoenicis.javafx.components.application.skin.ApplicationInformationPanelSkin.lambda$installScript$7(ApplicationInformationPanelSkin.java:237)
        at org.phoenicis.scripts.session.PhoenicisInteractiveScriptSession.eval(PhoenicisInteractiveScriptSession.java:35)
        at org.phoenicis.scripts.interpreter.BackgroundScriptInterpreter.lambda$createInteractiveSession$1(BackgroundScriptInterpreter.java:45)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
        at java.base/java.lang.Thread.run(Thread.java:835)

when I try to do

.preInstall(function() {
        const graphicsProperties = Bean("graphicsPropertiesFetcher").GetProperties();
        print(graphicsProperties.getVendor());
        print(graphicsProperties.getOpenGLVersion());
        print(graphicsProperties.getVulkanVersion());
    });

ImperatorS79 avatar Aug 24 '19 15:08 ImperatorS79

@Zemogiter can you open a PR with your changes. It is quite hard to help debugging this without knowing how exactly your Java code looks :)

madoar avatar Aug 24 '19 16:08 madoar

wrong person ^^, but the java code is in one of the comment

ImperatorS79 avatar Aug 24 '19 16:08 ImperatorS79

But

.preInstall(function() {
        const graphicsProperties = Bean("graphicsPropertiesFetcher").GetProperties();
        print(graphicsProperties.toString());
    });

is working correctly?

madoar avatar Aug 24 '19 16:08 madoar

Yes

ImperatorS79 avatar Aug 24 '19 16:08 ImperatorS79

Try recompiling again maybe the additional getter methods are not correctly added to the class...

madoar avatar Aug 24 '19 16:08 madoar

Maybe it is this ? It is not in the toString method.

ImperatorS79 avatar Aug 24 '19 16:08 ImperatorS79

@madoar Could it be that I call the method on the returned object and not in the call directly ? It is strange that js cannot manage such a simple structure.

ImperatorS79 avatar Aug 24 '19 16:08 ImperatorS79

Could it be that I call the method on the returned object and not in the call directly ?

What do you mean?

madoar avatar Aug 24 '19 16:08 madoar

const graphicsProperties = Bean("graphicsPropertiesFetcher").GetProperties();
print(graphicsProperties.getVendor());

vs

print(Bean("graphicsPropertiesFetcher").GetProperties().getVendor());

ImperatorS79 avatar Aug 24 '19 16:08 ImperatorS79

This should make no difference. There is something going really wrong with the returned object. Can you try:

const graphicsProperties = Bean("graphicsPropertiesFetcher").GetProperties();

print(graphicsProperties);
print(graphicsProperties.getClass());
print(graphicsProperties.getClass().getSimpleName());

and tell us the results?

madoar avatar Aug 24 '19 16:08 madoar

Intel Open Source Technology Center 3.0 1.1.0
class org.phoenicis.tools.system.GraphicsProperties
GraphicsProperties

ImperatorS79 avatar Aug 24 '19 16:08 ImperatorS79

Can you try:

print("Methods:");
graphicsProperties.getClass().getDeclaredMethods().foreach(method => {
   print(method.getName());
});
print("Fields:");
graphicsProperties.getClass().getDeclaredFields().foreach(field => {
   print(field.getName());
});

madoar avatar Aug 24 '19 16:08 madoar

I got:

Methods:
[ERROR] org.phoenicis.multithreading.ControlledThreadPoolExecutorService (l.64) - TypeError: undefined is not a function
        at <js> :anonymous(Unnamed:19-21:719-836)
        at <js> go(Unnamed:81:3209-3243)
        at org.graalvm.truffle/com.oracle.truffle.polyglot.ObjectProxyHandler.invoke(HostInteropReflect.java:678)
        at com.sun.proxy.$Proxy43.go(Unknown Source)
        at org.phoenicis.javafx.components.application.skin.ApplicationInformationPanelSkin.lambda$installScript$7(ApplicationInformationPanelSkin.java:237)
        at org.phoenicis.scripts.session.PhoenicisInteractiveScriptSession.eval(PhoenicisInteractiveScriptSession.java:35)
        at org.phoenicis.scripts.interpreter.BackgroundScriptInterpreter.lambda$createInteractiveSession$1(BackgroundScriptInterpreter.java:45)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
        at java.base/java.lang.Thread.run(Thread.java:835)

Exception in thread "pool-3-thread-4" TypeError: undefined is not a function
        at <js> :anonymous(Unnamed:19-21:719-836)
        at <js> go(Unnamed:81:3209-3243)
        at org.graalvm.truffle/com.oracle.truffle.polyglot.ObjectProxyHandler.invoke(HostInteropReflect.java:678)
        at com.sun.proxy.$Proxy43.go(Unknown Source)
        at org.phoenicis.javafx.components.application.skin.ApplicationInformationPanelSkin.lambda$installScript$7(ApplicationInformationPanelSkin.java:237)
        at org.phoenicis.scripts.session.PhoenicisInteractiveScriptSession.eval(PhoenicisInteractiveScriptSession.java:35)
        at org.phoenicis.scripts.interpreter.BackgroundScriptInterpreter.lambda$createInteractiveSession$1(BackgroundScriptInterpreter.java:45)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
        at java.base/java.lang.Thread.run(Thread.java:835)

ImperatorS79 avatar Aug 24 '19 16:08 ImperatorS79

Can you try forEach instead?

madoar avatar Aug 24 '19 17:08 madoar

Methods:
getVendor
getOpenGLVersion
getVulkan
toString
Fields:
Vendor
OpenGLVersion
VulkanVersion

ImperatorS79 avatar Aug 24 '19 17:08 ImperatorS79

Then you should be able to access any of these methods and fields from inside JS. Have you defined GraphicsProperties in its own *.java file or is it an inner class of GraphicsProperties? Can you please move it to its own file.

In addition try using lower-case field names, e.g. vendor, openGLVersion etc.

madoar avatar Aug 25 '19 08:08 madoar