Wrong draw order using ParaView's pvbatch in an Arch Linux Docker container on GitLab CI

I am using the command line interface pvbatch of ParaView to render PNG screenshots of my data. If I do so on my local machine, it runs fine, the result is as expected. However, if I run the same script in the Arch Linux Docker container via GitLab CI, the drawing order is not respected.

In this minimal example, we have two data sets in XDMF format:

The script screenshot.pvbatch.py outputs screenshot.png. This is what it should look like, and what it in fact looks like when I run the given script on my local machine:

screenshot

However, when I run the same script in the Docker container via GitLab CI according to .gitlab-ci.yaml, I instead get this result, where the front point is wrongly drawn behind the plane.

screenshot

Again, this is my pvbatch script:

from paraview.simple import *
LoadState('state.pvsm')
renderView = GetActiveViewOrCreate('RenderView')
renderView.ViewSize = [300, 300]
SaveScreenshot('screenshot.png', view=renderView)

And this is my GitLab CI configuration:

image: archlinux

render:
  script:
    - pacman -Sy --noconfirm paraview xorg-server-xvfb
    - xvfb-run -a -s "-screen 0 1920x1080x24" -- pvbatch screenshot.pvbatch.py
  artifacts:
    paths:
      - screenshot.png

This is the command line output of pvbatch:

$ xvfb-run -a -s "-screen 0 1920x1080x24" -- pvbatch screenshot.pvbatch.py
[runner-wxekhkry-project-17162-concurrent-0-mv2i3dvd:01681] mca_base_component_repository_open: unable to open mca_accelerator_cuda: libcuda.so.1: cannot open shared object file: No such file or directory (ignored)
[runner-wxekhkry-project-17162-concurrent-0-mv2i3dvd:01681] mca_base_component_repository_open: unable to open mca_accelerator_rocm: libamdhip64.so.6: cannot open shared object file: No such file or directory (ignored)
[runner-wxekhkry-project-17162-concurrent-0-mv2i3dvd:01681] mca_base_component_repository_open: unable to open mca_rcache_gpusm: libcuda.so.1: cannot open shared object file: No such file or directory (ignored)
[runner-wxekhkry-project-17162-concurrent-0-mv2i3dvd:01681] mca_base_component_repository_open: unable to open mca_rcache_rgpusm: libcuda.so.1: cannot open shared object file: No such file or directory (ignored)
[openvkl] application requested ISPC device width 16via device name cpu_16
[openvkl] CPU device instantiated with width: 16, ISA: AVX512SKX
MESA: error: ZINK: vkCreateInstance failed (VK_ERROR_INCOMPATIBLE_DRIVER)
glx: failed to create drisw screen
failed to load driver: zink
MESA: error: ZINK: vkCreateInstance failed (VK_ERROR_INCOMPATIBLE_DRIVER)
glx: failed to create drisw screen
failed to load driver: zink

So now I wonder, how I can fix this issue, such that my data is drawn correctly.

Any help is very much appreciated!

Sounds like a driver/xvfb issue to me. Could you try using EGL release instead ?

I have installed the EGL release, now pvbatch runs without any errors.
However, the issue remains: The plane is always drawn on top of the two points, even though one of the two points is in front of the plane.

Updated .gitlab-ci.yml:

image: archlinux

render:
  script:
    - pacman -Sy --noconfirm intel-tbb openmpi ffmpeg adios2 liblas ospray pdal python-numpy protobuf cgns double-conversion expat freetype2 gdal gl2ps glew hdf5 libjpeg jsoncpp libharu libxml2 lz4 xz python-mpi4py netcdf libogg libpng pugixml libtheora libtiff zlib verdict gegl libxcrypt-compat
    - curl 'https://www.paraview.org/paraview-downloads/download.php?submit=Download&version=v5.11&type=binary&os=Linux&downloadFile=ParaView-5.11.2-egl-MPI-Linux-Python3.9-x86_64.tar.gz' -o paraview.tar.gz
    - tar --strip-components=1 -xzf paraview.tar.gz -C /
    - pvbatch screenshot.pvbatch.py
  artifacts:
    paths:
      - screenshot.png

.gitlab-ci.yml (680 Bytes)

Looks like a bug when points are rendered as spheres.

With points not rendered as spheres, it works even in the container:
screenshot
For my usecase though, this is not quite the visuals I am going for…

I’m having the same issue with lines rendered as tubes. Some of the lines in this picture should be on the front and some of the back of this contoured shape.
screenshot_20240214_095933

did you try with another base image, eg Ubuntu ?

I think this is a long time bug in VTK when EGL is used. A quick search in VTK issue tracker gives https://gitlab.kitware.com/vtk/vtk/-/issues/16031 which seems related to draw order (see description - “lines shouldn’t appear behind labels.”)

Original issue was with X version and Xvfb.

Can you share output of the command ./bin/vtkProbeOpenGLVersion-pv5.11? This executable should be in your bin directory of Paraview.

Here’s sample output from a mac:

$  ./bin/vtkProbeOpenGLVersion-pv5.12 

Class: vtkCocoaRenderWindow succeeded in finding a working OpenGL

OpenGL vendor string:  Apple
OpenGL renderer string:  Apple M1
OpenGL version string:  4.1 Metal - 88
OpenGL extensions:  
  GL_ARB_blend_func_extended
  GL_ARB_draw_buffers_blend
  GL_ARB_draw_indirect
  GL_ARB_ES2_compatibility
  GL_ARB_explicit_attrib_location
  GL_ARB_gpu_shader_fp64
  GL_ARB_gpu_shader5
  GL_ARB_instanced_arrays
  GL_ARB_internalformat_query
  GL_ARB_occlusion_query2
  GL_ARB_sample_shading
  GL_ARB_sampler_objects
  GL_ARB_separate_shader_objects
  GL_ARB_shader_bit_encoding
  GL_ARB_shader_subroutine
  GL_ARB_shading_language_include
  GL_ARB_tessellation_shader
  GL_ARB_texture_buffer_object_rgb32
  GL_ARB_texture_cube_map_array
  GL_ARB_texture_gather
  GL_ARB_texture_query_lod
  GL_ARB_texture_rgb10_a2ui
  GL_ARB_texture_storage
  GL_ARB_texture_swizzle
  GL_ARB_timer_query
  GL_ARB_transform_feedback2
  GL_ARB_transform_feedback3
  GL_ARB_vertex_attrib_64bit
  GL_ARB_vertex_type_2_10_10_10_rev
  GL_ARB_viewport_array
  GL_EXT_debug_label
  GL_EXT_debug_marker
  GL_EXT_framebuffer_multisample_blit_scaled
  GL_EXT_texture_compression_s3tc
  GL_EXT_texture_filter_anisotropic
  GL_EXT_texture_sRGB_decode
  GL_APPLE_client_storage
  GL_APPLE_container_object_shareable
  GL_APPLE_flush_render
  GL_APPLE_rgb_422
  GL_APPLE_row_bytes
  GL_APPLE_texture_range
  GL_NV_texture_barrier
PixelFormat Descriptor:
  colorSize:  32
  alphaSize:  8
  stencilSize:  0
  depthSize:  32
  accumSize:  0
  double buffer:  Yes
  stereo:  No
  stencil:  0
  hardware acceleration:  Yes
  profile version:  0x4100

I’m interested in the value of depthSize inside your container, both with xvfb and egl. Anything below 24 is insufficient.

Rendering points as spheres and lines as tubes, both of those features need read/write access to the depth buffer which is later used by order independent translucent pass to ensure correct draw order. Wonder if the depth buffer is even created in your container?

Original issue was with X version and Xvfb.

Looks like for X and EGL, VTK asks for a minimum depth buffer size of 1 and 8 respectively. x11, egl

Whereas on windows and macOS, VTK enforces a minimum depth size of 24, 32 respectively. windows, mac

I think something in their container messes up depth buffer. Will never know for sure unless confirmed by running the VTK OpenGL probe executable inside it.

No idea why these numbers were chosen in particular. Cc @Sankhesh_Jhaveri

1 Like

I don’t know either but I’d guess it was by trial and error :man_shrugging:

Well, I tried that, but that leads to another, unrelated issue: Pvbatch does not find the paraview.simple module.

Here the .gitlab-ci.yml that leads to this error:

image: ubuntu

render:
  script:
    - apt-get update
    - apt-get upgrade -y
    - DEBIAN_FRONTEND=noninteractive apt-get install -y xorg xvfb paraview
    - xvfb-run -a -s "-screen 0 1920x1080x24" -- pvbatch screenshot.pvbatch.py
  artifacts:
    paths:
      - screenshot.png

Note: DEBIAN_FRONTEND=noninteractive has been set to use default values when installing tzdata and other packages that require user input during installation.

After installation, pvbatch fails to find the paraview.simple package:

$ xvfb-run -a -s "-screen 0 1920x1080x24" -- pvbatch screenshot.pvbatch.py
Traceback (most recent call last):
  File "/builds/u0143112/test-paraview-gitlab-ci/screenshot.pvbatch.py", line 2, in <module>
    from paraview.simple import *
ModuleNotFoundError: No module named 'paraview'
Cleaning up project directory and file based variables
ERROR: Job failed: command terminated with exit code 1

In the Arch Linux container, I get this output for vtkProbeOpenGLVersion*:

$ xvfb-run -a -s "-screen 0 1920x1080x24" -- find / -iname 'vtkProbeOpenGLVersion*' -exec '{}' \;
MESA: error: ZINK: vkCreateInstance failed (VK_ERROR_INCOMPATIBLE_DRIVER)
glx: failed to create drisw screen
failed to load driver: zink
MESA: error: ZINK: vkCreateInstance failed (VK_ERROR_INCOMPATIBLE_DRIVER)
glx: failed to create drisw screen
failed to load driver: zink
Class: vtkXOpenGLRenderWindow succeeded in finding a working OpenGL
server glx vendor string:  SGI
server glx version string:  1.4
server glx extensions:  GLX_ARB_context_flush_control GLX_ARB_create_context GLX_ARB_create_context_no_error GLX_ARB_create_context_profile GLX_ARB_fbconfig_float GLX_ARB_framebuffer_sRGB GLX_ARB_multisample GLX_EXT_create_context_es_profile GLX_EXT_create_context_es2_profile GLX_EXT_fbconfig_packed_float GLX_EXT_framebuffer_sRGB GLX_EXT_get_drawable_type GLX_EXT_libglvnd GLX_EXT_no_config_context GLX_EXT_texture_from_pixmap GLX_EXT_visual_info GLX_EXT_visual_rating GLX_MESA_copy_sub_buffer GLX_OML_swap_method GLX_SGI_make_current_read GLX_SGIS_multisample GLX_SGIX_fbconfig GLX_SGIX_pbuffer GLX_SGIX_visual_select_group 
client glx vendor string:  Mesa Project and SGI
client glx version string:  1.4
glx extensions:  GLX_ARB_context_flush_control GLX_ARB_create_context GLX_ARB_create_context_no_error GLX_ARB_create_context_profile GLX_ARB_fbconfig_float GLX_ARB_framebuffer_sRGB GLX_ARB_get_proc_address GLX_ARB_multisample GLX_EXT_create_context_es2_profile GLX_EXT_create_context_es_profile GLX_EXT_fbconfig_packed_float GLX_EXT_framebuffer_sRGB GLX_EXT_no_config_context GLX_EXT_texture_from_pixmap GLX_EXT_visual_info GLX_EXT_visual_rating GLX_MESA_copy_sub_buffer GLX_MESA_query_renderer GLX_SGIS_multisample GLX_SGIX_fbconfig GLX_SGIX_pbuffer GLX_SGIX_visual_select_group GLX_SGI_make_current_read 
OpenGL vendor string:  Mesa
OpenGL renderer string:  llvmpipe (LLVM 16.0.6, 256 bits)
OpenGL version string:  4.5 (Core Profile) Mesa 24.0.1-arch1.1
OpenGL extensions:  
  GL_3DFX_texture_compression_FXT1
  GL_AMD_conservative_depth
  GL_AMD_draw_buffers_blend
  GL_AMD_gpu_shader_int64
  GL_AMD_multi_draw_indirect
  GL_AMD_pinned_memory
  GL_AMD_query_buffer_object
  GL_AMD_seamless_cubemap_per_texture
  GL_AMD_shader_stencil_export
  GL_AMD_shader_trinary_minmax
  GL_AMD_texture_texture4
  GL_AMD_vertex_shader_layer
  GL_AMD_vertex_shader_viewport_index
  GL_ANGLE_texture_compression_dxt3
  GL_ANGLE_texture_compression_dxt5
  GL_ARB_ES2_compatibility
  GL_ARB_ES3_1_compatibility
  GL_ARB_ES3_2_compatibility
  GL_ARB_ES3_compatibility
  GL_ARB_arrays_of_arrays
  GL_ARB_base_instance
  GL_ARB_blend_func_extended
  GL_ARB_buffer_storage
  GL_ARB_clear_buffer_object
  GL_ARB_clear_texture
  GL_ARB_clip_control
  GL_ARB_compressed_texture_pixel_storage
  GL_ARB_compute_shader
  GL_ARB_conditional_render_inverted
  GL_ARB_conservative_depth
  GL_ARB_copy_buffer
  GL_ARB_copy_image
  GL_ARB_cull_distance
  GL_ARB_debug_output
  GL_ARB_depth_buffer_float
  GL_ARB_depth_clamp
  GL_ARB_derivative_control
  GL_ARB_direct_state_access
  GL_ARB_draw_buffers
  GL_ARB_draw_buffers_blend
  GL_ARB_draw_elements_base_vertex
  GL_ARB_draw_indirect
  GL_ARB_draw_instanced
  GL_ARB_enhanced_layouts
  GL_ARB_explicit_attrib_location
  GL_ARB_explicit_uniform_location
  GL_ARB_fragment_coord_conventions
  GL_ARB_fragment_layer_viewport
  GL_ARB_fragment_shader
  GL_ARB_framebuffer_no_attachments
  GL_ARB_framebuffer_object
  GL_ARB_framebuffer_sRGB
  GL_ARB_get_program_binary
  GL_ARB_get_texture_sub_image
  GL_ARB_gl_spirv
  GL_ARB_gpu_shader5
  GL_ARB_gpu_shader_fp64
  GL_ARB_gpu_shader_int64
  GL_ARB_half_float_pixel
  GL_ARB_half_float_vertex
  GL_ARB_indirect_parameters
  GL_ARB_instanced_arrays
  GL_ARB_internalformat_query
  GL_ARB_internalformat_query2
  GL_ARB_invalidate_subdata
  GL_ARB_map_buffer_alignment
  GL_ARB_map_buffer_range
  GL_ARB_multi_bind
  GL_ARB_multi_draw_indirect
  GL_ARB_occlusion_query2
  GL_ARB_parallel_shader_compile
  GL_ARB_pipeline_statistics_query
  GL_ARB_pixel_buffer_object
  GL_ARB_point_sprite
  GL_ARB_polygon_offset_clamp
  GL_ARB_post_depth_coverage
  GL_ARB_program_interface_query
  GL_ARB_provoking_vertex
  GL_ARB_query_buffer_object
  GL_ARB_robust_buffer_access_behavior
  GL_ARB_robustness
  GL_ARB_sample_shading
  GL_ARB_sampler_objects
  GL_ARB_seamless_cube_map
  GL_ARB_seamless_cubemap_per_texture
  GL_ARB_separate_shader_objects
  GL_ARB_shader_atomic_counter_ops
  GL_ARB_shader_atomic_counters
  GL_ARB_shader_ballot
  GL_ARB_shader_bit_encoding
  GL_ARB_shader_clock
  GL_ARB_shader_draw_parameters
  GL_ARB_shader_group_vote
  GL_ARB_shader_image_load_store
  GL_ARB_shader_image_size
  GL_ARB_shader_objects
  GL_ARB_shader_precision
  GL_ARB_shader_stencil_export
  GL_ARB_shader_storage_buffer_object
  GL_ARB_shader_subroutine
  GL_ARB_shader_texture_image_samples
  GL_ARB_shader_texture_lod
  GL_ARB_shader_viewport_layer_array
  GL_ARB_shading_language_420pack
  GL_ARB_shading_language_include
  GL_ARB_shading_language_packing
  GL_ARB_spirv_extensions
  GL_ARB_stencil_texturing
  GL_ARB_sync
  GL_ARB_tessellation_shader
  GL_ARB_texture_barrier
  GL_ARB_texture_border_clamp
  GL_ARB_texture_buffer_object
  GL_ARB_texture_buffer_object_rgb32
  GL_ARB_texture_buffer_range
  GL_ARB_texture_compression_bptc
  GL_ARB_texture_compression_rgtc
  GL_ARB_texture_cube_map_array
  GL_ARB_texture_filter_anisotropic
  GL_ARB_texture_filter_minmax
  GL_ARB_texture_float
  GL_ARB_texture_gather
  GL_ARB_texture_mirror_clamp_to_edge
  GL_ARB_texture_multisample
  GL_ARB_texture_non_power_of_two
  GL_ARB_texture_query_levels
  GL_ARB_texture_query_lod
  GL_ARB_texture_rectangle
  GL_ARB_texture_rg
  GL_ARB_texture_rgb10_a2ui
  GL_ARB_texture_stencil8
  GL_ARB_texture_storage
  GL_ARB_texture_storage_multisample
  GL_ARB_texture_swizzle
  GL_ARB_texture_view
  GL_ARB_timer_query
  GL_ARB_transform_feedback2
  GL_ARB_transform_feedback3
  GL_ARB_transform_feedback_instanced
  GL_ARB_transform_feedback_overflow_query
  GL_ARB_uniform_buffer_object
  GL_ARB_vertex_array_bgra
  GL_ARB_vertex_array_object
  GL_ARB_vertex_attrib_64bit
  GL_ARB_vertex_attrib_binding
  GL_ARB_vertex_buffer_object
  GL_ARB_vertex_shader
  GL_ARB_vertex_type_10f_11f_11f_rev
  GL_ARB_vertex_type_2_10_10_10_rev
  GL_ARB_viewport_array
  GL_ARM_shader_framebuffer_fetch_depth_stencil
  GL_ATI_blend_equation_separate
  GL_ATI_meminfo
  GL_ATI_texture_float
  GL_ATI_texture_mirror_once
  GL_EXT_EGL_image_storage
  GL_EXT_EGL_sync
  GL_EXT_abgr
  GL_EXT_blend_equation_separate
  GL_EXT_debug_label
  GL_EXT_draw_buffers2
  GL_EXT_draw_instanced
  GL_EXT_framebuffer_blit
  GL_EXT_framebuffer_multisample
  GL_EXT_framebuffer_multisample_blit_scaled
  GL_EXT_framebuffer_object
  GL_EXT_framebuffer_sRGB
  GL_EXT_memory_object
  GL_EXT_memory_object_fd
  GL_EXT_packed_depth_stencil
  GL_EXT_packed_float
  GL_EXT_pixel_buffer_object
  GL_EXT_polygon_offset_clamp
  GL_EXT_provoking_vertex
  GL_EXT_shader_framebuffer_fetch
  GL_EXT_shader_framebuffer_fetch_non_coherent
  GL_EXT_shader_image_load_formatted
  GL_EXT_shader_integer_mix
  GL_EXT_texture_array
  GL_EXT_texture_compression_dxt1
  GL_EXT_texture_compression_rgtc
  GL_EXT_texture_compression_s3tc
  GL_EXT_texture_filter_anisotropic
  GL_EXT_texture_filter_minmax
  GL_EXT_texture_integer
  GL_EXT_texture_mirror_clamp
  GL_EXT_texture_sRGB
  GL_EXT_texture_sRGB_R8
  GL_EXT_texture_sRGB_RG8
  GL_EXT_texture_sRGB_decode
  GL_EXT_texture_shadow_lod
  GL_EXT_texture_shared_exponent
  GL_EXT_texture_snorm
  GL_EXT_texture_swizzle
  GL_EXT_timer_query
  GL_EXT_transform_feedback
  GL_EXT_vertex_array_bgra
  GL_EXT_vertex_attrib_64bit
  GL_IBM_multimode_draw_arrays
  GL_INTEL_shader_atomic_float_minmax
  GL_KHR_blend_equation_advanced
  GL_KHR_blend_equation_advanced_coherent
  GL_KHR_context_flush_control
  GL_KHR_debug
  GL_KHR_no_error
  GL_KHR_parallel_shader_compile
  GL_KHR_robust_buffer_access_behavior
  GL_KHR_robustness
  GL_KHR_texture_compression_astc_ldr
  GL_KHR_texture_compression_astc_sliced_3d
  GL_MESA_framebuffer_flip_y
  GL_MESA_pack_invert
  GL_MESA_shader_integer_functions
  GL_MESA_texture_signed_rgba
  GL_MESA_ycbcr_texture
  GL_NVX_gpu_memory_info
  GL_NV_conditional_render
  GL_NV_copy_image
  GL_NV_depth_clamp
  GL_NV_packed_depth_stencil
  GL_NV_shader_atomic_float
  GL_NV_texture_barrier
  GL_OES_EGL_image
  GL_S3_s3tc
X Extensions:  Generic Event Extension, SHAPE, MIT-SHM, XInputExtension, XTEST, BIG-REQUESTS, SYNC, XKEYBOARD, XC-MISC, SECURITY, XFIXES, RENDER, RANDR, XINERAMA, Composite, DAMAGE, MIT-SCREEN-SAVER, DOUBLE-BUFFER, RECORD, Present, X-Resource, XVideo, GLX

This is the CI config for this:

image: archlinux

render:
  script:
    - pacman -Sy --noconfirm paraview xorg-server-xvfb
    - xvfb-run -a -s "-screen 0 1920x1080x24" -- find / -iname 'vtkProbeOpenGLVersion*' -exec '{}' \;
    - xvfb-run -a -s "-screen 0 1920x1080x24" -- pvbatch screenshot.pvbatch.py
  artifacts:
    paths:
      - screenshot.png

The find command tries to find all binaries on the system with this name. There is no binary that has the exact name that you asked for (./bin/vtkProbeOpenGLVersion-pv5.11).

Okay. Looks like the egl distributions of paraview do not have that binary. Can you run this python script with pvpython instead?

import vtk
import sys

renderer = vtk.vtkRenderer()
renderWindow  = vtk.vtkRenderWindow()
renderWindow.AddRenderer(renderer)
renderWindow.Render()

print(f"Class: {renderWindow.GetClassName()}")
print(f"depthSize: {renderWindow.GetDepthBufferSize()}")

I have saved your code in script depthsize.pvpython.py:

I ran it with pvpython with this configuration:

image: archlinux

render:
  script:
    - pacman -Sy --noconfirm paraview xorg-server-xvfb
    - xvfb-run -a -s "-screen 0 1920x1080x24" -- pvpython depthsize.pvpython.py
    - xvfb-run -a -s "-screen 0 1920x1080x24" -- pvbatch screenshot.pvbatch.py
  artifacts:
    paths:
      - screenshot.png

It returns a depthSize of 32:

Class: vtkXOpenGLRenderWindow
depthSize: 32

Okay. That sort of rules out a missing depth buffer. With EGL paraview I expected to see

Class: vtkEGLRenderWindow
depthSize: 24

I am still wondering if there is a fix for this issue or a possible workaround.

Have you tried the EGL release on ubuntu? I guess it may be an xvfb-run issue on arch linux?

Thank you! With the EGL release on Ubuntu, I was able to get it to run and render the example correctly without any warnings or errors! The CI configuration now looks like this:

image: ubuntu

render:
  script:
    - apt-get update
    - apt-get upgrade -y
    - DEBIAN_FRONTEND=noninteractive apt-get install -y xorg xvfb curl libavcodec58 libavformat58 libavutil56 libc6 libdouble-conversion3 libexpat1 libfreetype6 libgcc-s1 libgdal30 libgl2ps1.4 libglew2.2 libglx0 libhdf5-103-1 libjpeg8 liblz4-1 liblzma5 libnetcdf19 libopengl0 libopenmpi3 libpdal-base13 libpng16-16 libpython3.10 libqt5core5a libqt5gui5 libqt5help5 libqt5network5 libqt5widgets5 libstdc++6 libswscale5 libtiff5 libx11-6 libxcursor1 libxml2 python3-autobahn python3-matplotlib python3-mpi4py python3-six python3-twisted tclsh zlib1g mpi-default-bin paraview-doc python3-paraview h5utils hdf5-tools
    - curl 'https://www.paraview.org/paraview-downloads/download.php?submit=Download&version=v5.10&type=binary&os=Linux&downloadFile=ParaView-5.10.0-egl-MPI-Linux-Python3.9-x86_64.tar.gz' -o paraview.tar.gz
    - mkdir -p /opt/paraview
    - tar --strip-components=1 -xzf paraview.tar.gz -C /opt/paraview
    - export PATH=/opt/paraview/bin:$PATH
    - xvfb-run -a -s "-screen 0 1920x1080x24" -- pvbatch screenshot.pvbatch.py
  artifacts:
    paths:
      - screenshot.png

(Note: I am not sure which of the packages I install via apt are actually required for the EGL package.)

I will likely play around with it a bit more to figure out if I can get this to run in the archlinux Docker image.