This is a follow-up post from an earlier guest post by Blu about OpenGL ES development on Chrome OS.
One can’t practice real-time rendering to disk files for long ‒ it’s just unnatural. So after checking that my habitual GLES tests work as intended on ChromeOS when rendering to an off-screen-buffer-subsequently-saved-to-a-PNG, the next step was to figure out a way how to show frames on screen at a palpable framerate, if possible.
Being as new to Chrome OS as the next guy, I had to start from scratch with ‘How to show EGL surfaces on screen fast’. In the comments section to the first article William Barath kindly mentioned that there was a wayland client library on Chromebrew, so I decided to pursue that as I had had (positive) prior experience with wayland.
Long story short, the established way on most platforms for connecting wayland to EGL (or vice versa) is to ask wayland/weston for an EGL-compatible window surface, and then pass that to EGL as a native window for EGL to set that up as the main framebuffer. Alas, it tuned out ChromeOS-native EGL does not work with the wayland surface format provided by the available wayland interfaces, so that low-friction path ended abruptly. BTW, that comment is not entirely correct ‒ the issue was not in the mismatch of native-window objects and EGL drawable configs ‒ that would produce error code EGL_BAD_MATCH; the issue was that EGL did not recognize the supplied native window as such ‒ error code EGL_BAD_NATIVE_WINDOW.
With the straight-forward EGL-on-wayland approach a no-go I had to look elsewhere. After inspecting the protocols provided by wayland server on ChromeOS, one particular protocol caught my attention: zwp_linux_dmabuf_v1. Turns out that protocol was originally designed as a wayland counterpart to a particular EGL extensions for taking linux DMA buffers and turning those into usable EGLImages.. How convenient! The name of the extension is EGL_EXT_image_dma_buf_import, and it is gaining popularity across various EGL-enabled platforms, ChromeOS included.
But then came the next obstacle ‒ the wayland client libs found in Chromebew did not expose any client-side zwp_linux_dmabuf_v1 functionality. For that I turned for help to the Chromebrew folks, but as it was not the most trivial of requests promising a quick resolution, I simultaneously continued with plan B ‒ tweak the GLES-to-PNG frameloop into a GLES-to-wayland-shared-mem-buffer one. Crude but effective. Moreover, that would be only a temporary solution anyway.
Here it needs mentioning that wayland has rather poorly documented client-side APIs, and independent researchers’ material like Jan Newmarch’s Programming Wayland Clients turned indispensable. Also Henrique Dante de Almeida’s hello-wayland tutorial proved a good starting point.
Long story short, the first outcome from this partially-manual-partially-automated plan-B presentation scheme looked like this:
If not well visible in the video: that’s 60fps with some stutter, per a CPU load of 30%. Here is the repo that goes along with it. Points of potential interest follow.
Firstly, it really takes a bit of luck for such a crude presentation scheme to not be a total disaster. Things like mismatching pixel formats and disagreeing buffer strides could affect it greatly. Surely any of those could be fixed on-the-fly by the CPU, but in a situation where the CPU should not be having any business interfering in the first place, any additional CPU fix-ups could pile up to a serious real-time debt.
GLES’ glReadPixels API and wayland’s wl_shm_pool_create_buffer API’s offer a fair bit of overlap in pixel formats, so the goal there was to find a GLES pixel format of least overhead when converting the internal framebuffer format to the format expected by wayland, whatever that might be. Luckily, GLES’ glReadPixels(format = GL_RGBA, type = GL_UNSIGNED_BYTE) turned out fairly friction-less for RGBA8 internal formats, and matched perfectly to one of wayland’s SHM buffer formats ‒ WL_SHM_FORMAT_ABGR8888 ‒ spending precious CPU cycles in conversions avoided. The fact the GLES framebuffer showed up upside-down in the wayland SHM buffer was but a minor detail that could be fixed at no-cost in the GLES code (by flipping a sign in the projection matrix along with a GLES tri-winding control bit). Alternatively, I could simply show scenes without apparent up/down orientation ; ]
Another tricky point was coming up with a frameloop order of doing things that does not totally suck (™) but there I just had to follow the rule of thumb that any access to a framebuffer should not immediately follow the preceding drawing to that framebuffer. There also helped the fact I didn’t need to double-buffer EGL-side, as wayland’s surfaces are already double-buffered server-side.
That said, this frameloop is still extremely sensitive to CPU performance, so making more than a couple of draw calls (i.e. the major CPU cycle eaters in most GLES scenarios) per frame could result in notable diminishes in framerate.
But most importantly I can use my Chromebook for GLES development now ‒ prototyping shaders, tuning math routines ‒ all things I otherwise expect from my everyday notebooks. With some luck this quick’n’dirty EGL/wayland patchwork could develop into a proper zero-CPU-overhead frameloop suitable for more complex applications /fingers crossed
BTW, as an unexpected bonus now I can see the machine-clock estimates of GLSL shaders ‒ apparently a standard debug feature of Imagination Technologies’ shader compiler found on my Chromebook. Neat.
|Support CNX Software - Donate via PayPal or become a Patron on Patreon|