I will post an answer myself. It works well both on Linux desktop and on the embedded system, with good performance so far as I can see.
The idea is taking an EGL context and GLES, and setting AWT canvas surface as a direct target for the context. You don't need to read buffer and then paint it on the canvas.
Below is my Window class initialization.
private Canvas canvas;
private JAWTDrawingSurface ds;
public long display;
private long eglDisplay;
public long drawable;
private long surface;
public static final JAWT awt;
static {
awt = JAWT.calloc();
awt.version(JAWT_VERSION_1_4);
if (!JAWT_GetAWT(awt))
throw new AssertionError("GetAWT failed");
}
public void lock() throws AWTException {
int lock = JAWT_DrawingSurface_Lock(ds, ds.Lock());
if ((lock & JAWT_LOCK_ERROR) != 0)
throw new AWTException("JAWT_DrawingSurface_Lock() failed");
}
public void unlock() throws AWTException {
JAWT_DrawingSurface_Unlock(ds, ds.Unlock());
}
public void Init2()
{
frame = new JFrame("AWT test");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.setPreferredSize(new Dimension(width, height));
canvas = new Canvas();
frame.add(canvas);
frame.pack();
frame.setVisible(true);
frame.transferFocus();
int error;
System.out.println("Window init2() started");
this.ds = JAWT_GetDrawingSurface(canvas, awt.GetDrawingSurface());
//JAWTDrawingSurface ds = JAWT_GetDrawingSurface(canvas, awt.GetDrawingSurface());
try
{
lock();
try
{
JAWTDrawingSurfaceInfo dsi = JAWT_DrawingSurface_GetDrawingSurfaceInfo(ds, ds.GetDrawingSurfaceInfo());
JAWTX11DrawingSurfaceInfo dsiWin = JAWTX11DrawingSurfaceInfo.create(dsi.platformInfo());
int depth = dsiWin.depth();
this.display = dsiWin.display();
this.drawable = dsiWin.drawable();
System.out.printf("EGL Display %d, drawable: \n", display, drawable);
eglDisplay = eglGetDisplay(display);
EGLCapabilities egl;
try (MemoryStack stack = stackPush()) {
IntBuffer major = stack.mallocInt(1);
IntBuffer minor = stack.mallocInt(1);
if (!eglInitialize(eglDisplay, major, minor)) {
throw new IllegalStateException(String.format("Failed to initialize EGL [0x%X]", eglGetError()));
}
egl = EGL.createDisplayCapabilities(eglDisplay, major.get(0), minor.get(0));
}
System.out.println("EGL caps created");
IntBuffer attrib_list = BufferUtils.createIntBuffer(18);
attrib_list.put(EGL_CONFORMANT).put(EGL_OPENGL_ES2_BIT);
//attrib_list.put(EGL_ALPHA_MASK_SIZE).put(4);
//attrib_list.put(EGL_ALPHA_SIZE).put(4);
//attrib_list.put(EGL_RED_SIZE).put(5);
//attrib_list.put(EGL_GREEN_SIZE).put(6);
//attrib_list.put(EGL_BLUE_SIZE).put(5);
//attrib_list.put(EGL_DEPTH_SIZE).put(4);
//attrib_list.put(EGL_SURFACE_TYPE).put(EGL_WINDOW_BIT);
attrib_list.put(EGL_NONE);
attrib_list.flip();
PointerBuffer fbConfigs = BufferUtils.createPointerBuffer(1);
IntBuffer numConfigs = BufferUtils.createIntBuffer(1);
boolean test2 = eglChooseConfig(eglDisplay, attrib_list, fbConfigs,numConfigs);
if (fbConfigs == null || fbConfigs.capacity() == 0) {
// No framebuffer configurations supported!
System.out.println("No supported framebuffer configurations found");
}
long test = numConfigs.get(0);
IntBuffer context_attrib_list = BufferUtils.createIntBuffer(18);
context_attrib_list.put(EGL_CONTEXT_MAJOR_VERSION).put(3);
context_attrib_list.put(EGL_CONTEXT_MINOR_VERSION).put(0);
context_attrib_list.put(EGL_NONE);
context_attrib_list.flip();
long context = eglCreateContext(eglDisplay,fbConfigs.get(0),EGL_NO_CONTEXT,context_attrib_list);
error = eglGetError();
surface = eglCreateWindowSurface(eglDisplay,fbConfigs.get(0),drawable,(int[])null);
error = eglGetError();
eglMakeCurrent(eglDisplay,surface,surface,context);
error = eglGetError();
GLESCapabilities gles = GLES.createCapabilities();
System.out.println("CLES caps created");
}
finally
{
unlock();
}
}
catch (Exception e)
{
System.out.println("JAWT Failed");
}
// Render with OpenGL ES
glClearColor(0f,0f,0f,1f);
glfwSwapInterval(vSync);
glEnable(GL_DEPTH_TEST);
int[] range = new int[2];
int[] precision = new int[2];
glGetShaderPrecisionFormat(GL_FRAGMENT_SHADER, GL_HIGH_FLOAT, range, precision);
System.out.println("Window context Initialized");
}
Note that attribute lists can be whatever you want or nothing at all. I actually ran into GLES problem though when I didn't specify 3.0 as version compatibility for the context - it tried to use OpenGl ES-CL 1.1 for some reason and failed. Anyway, simply specifying minor and major context version fixed it.
Also note that it's the initialization only. JFrame and Canvas are created elsewhere and Canvas then passed to the Window class constructor.
Another important thing is the lock() / unlock() functions. You will get lots of XCB errors and crashes without them - because other threads will try to update display while you're drawing, causing conflicts and XCB will crash.
Also, you need to lock and unlock when you swap buffers (i.e. when you actually draw the framebuffer.
public void update()
{
try
{
lock();
eglSwapBuffers(eglDisplay, surface);
unlock();
}
catch (Exception e)
{
System.out.println("Swap buffers failed");
}
}
Don't mind that my current exception handling is lacking - I am editing my answer as soon as I found the solution lest my previous version confuses people.
I would also like to give credit to lwjgl-awt project, for giving me ideas. It does not support EGL as is, so I had to modify it a bit, but I took parts from there, so credit where credit is due.
https://github.com/LWJGLX/lwjgl3-awt
Just for comparison sake, here is the GLFW version. It's the initialization of the same class, basically, but it simply does other things. Here, however, the window is created directly inside the method.
public void Init()
{
System.out.println("Window init() started");
GLFWErrorCallback.createPrint().set();
if (!glfwInit()) {
throw new IllegalStateException("Unable to initialize glfw");
}
glfwDefaultWindowHints();
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
// GLFW setup for EGL & OpenGL ES
glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
windowHandle = glfwCreateWindow(width, height, title, NULL, NULL);
if (windowHandle == NULL) {
throw new RuntimeException("Failed to create the GLFW window");
}
System.out.printf("Window handle created %d\n", windowHandle);
SetCallbacks();
// EGL capabilities
displayHandle = glfwGetEGLDisplay();
System.out.printf("EGL DisplayHandle %d\n", displayHandle);
try (MemoryStack stack = stackPush()) {
IntBuffer major = stack.mallocInt(1);
IntBuffer minor = stack.mallocInt(1);
if (!eglInitialize(displayHandle, major, minor)) {
throw new IllegalStateException(String.format("Failed to initialize EGL [0x%X]", eglGetError()));
}
EGLCapabilities egl = EGL.createDisplayCapabilities(displayHandle, major.get(0), minor.get(0));
}
System.out.println("EGL caps created");
// OpenGL ES capabilities
glfwMakeContextCurrent(windowHandle);
System.out.printf("Current context: %d.%d rev %d, Client_Context: %d\n",glfwGetWindowAttrib(windowHandle,GLFW_CONTEXT_VERSION_MAJOR),
glfwGetWindowAttrib(windowHandle,GLFW_CONTEXT_VERSION_MINOR), glfwGetWindowAttrib(windowHandle,GLFW_CONTEXT_REVISION),
glfwGetWindowAttrib(windowHandle,GLFW_CLIENT_API));
GLESCapabilities gles = GLES.createCapabilities();
System.out.println("CLES caps created");
// Render with OpenGL ES
//glfwShowWindow(windowHandle);
glClearColor(0f,0f,0f,1f);
glfwSwapInterval(vSync);
glEnable(GL_DEPTH_TEST);
int[] range = new int[2];
int[] precision = new int[2];
glGetShaderPrecisionFormat(GL_FRAGMENT_SHADER, GL_HIGH_FLOAT, range, precision);
System.out.printf("Current context: %d.%d\n",glfwGetWindowAttrib(windowHandle,GLFW_CONTEXT_VERSION_MAJOR),
glfwGetWindowAttrib(windowHandle,GLFW_CONTEXT_VERSION_MINOR));
}