Skip to content

try fix Failed to create /mesa_shader_cache for shader cache (Permiss… #9

try fix Failed to create /mesa_shader_cache for shader cache (Permiss…

try fix Failed to create /mesa_shader_cache for shader cache (Permiss… #9

Workflow file for this run

name: Test OpenGL Headless Environment
on:
push:
branches: [ ci-opengl-setup ]
pull_request:
branches: [ ci-opengl-setup ]
workflow_dispatch:
env:
# Headless Linux CI lacks hardware OpenGL and X11 display. These force Mesa software rendering, used alongside Xvfb - a virtual X11 server that we start before our tests.
LIBGL_ALWAYS_SOFTWARE: 1
MESA_LOADER_DRIVER_OVERRIDE: llvmpipe
jobs:
test-opengl:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install comprehensive Mesa and X11 development packages
run: |
echo "=== Installing comprehensive OpenGL and X11 development packages ==="
sudo apt-get update
sudo apt-get install -y \
xvfb \
x11-xserver-utils \
x11-utils \
x11-apps \
mesa-utils \
libgl1-mesa-dev \
libgl1-mesa-dri \
libglu1-mesa-dev \
libglx-mesa0 \
libglx-dev \
libegl1-mesa-dev \
libx11-dev \
libxext-dev \
libxrandr-dev \
libxinerama-dev \
libxcursor-dev \
libxi-dev \
libxmu-dev \
build-essential \
gcc \
pkg-config
echo "=== Verifying installed packages ==="
dpkg -l | grep -E "(xvfb|mesa|glu|libgl|libx11)" | head -20
- name: Set up environment and start Xvfb
run: |
echo "=== Setting up Mesa environment variables ==="
echo "LIBGL_ALWAYS_SOFTWARE=$LIBGL_ALWAYS_SOFTWARE"
echo "MESA_LOADER_DRIVER_OVERRIDE=$MESA_LOADER_DRIVER_OVERRIDE"
echo "=== Starting Xvfb virtual display server ==="
# Start Xvfb exactly like in the justfile test-ci
sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 &
XVFB_PID=$!
export DISPLAY=:99
echo "XVFB_PID=$XVFB_PID" >> $GITHUB_ENV
echo "DISPLAY=:99" >> $GITHUB_ENV
# Wait for Xvfb to fully start
sleep 3
echo "Xvfb started with PID: $XVFB_PID on display :99"
- name: Verify X11 and OpenGL environment
run: |
echo "=== Environment verification ==="
echo "DISPLAY=$DISPLAY"
echo "LIBGL_ALWAYS_SOFTWARE=$LIBGL_ALWAYS_SOFTWARE"
echo "MESA_LOADER_DRIVER_OVERRIDE=$MESA_LOADER_DRIVER_OVERRIDE"
echo "=== X11 server status ==="
ps aux | grep Xvfb | grep -v grep || echo "No Xvfb processes found"
echo "=== X11 display info ==="
xdpyinfo -display :99 | head -20 || echo "xdpyinfo failed"
echo "=== X11 connectivity test ==="
xwininfo -root -display :99 || echo "xwininfo failed"
echo "=== Mesa/GLX information ==="
glxinfo -display :99 | grep -E "OpenGL vendor|OpenGL renderer|OpenGL version|GLX version|direct rendering" || echo "glxinfo failed"
- name: Test OpenGL header availability
run: |
echo "=== Testing OpenGL headers and compilation ==="
# Check if headers exist
find /usr/include -name "gl.h" 2>/dev/null || echo "gl.h not found"
find /usr/include -name "glx.h" 2>/dev/null || echo "glx.h not found"
# Test basic compilation
echo "Testing basic OpenGL compilation..."
cat > simple_gl_test.c <<'EOF'
#include <GL/gl.h>
#include <stdio.h>
int main(void) {
printf("OpenGL headers compile successfully\n");
return 0;
}
EOF
gcc simple_gl_test.c -o simple_gl_test -lGL
./simple_gl_test
- name: Run comprehensive OpenGL context test
run: |
echo "=== Running comprehensive GLX context creation test ==="
cat > comprehensive_gl_test.c <<'EOF'
#include <GL/gl.h>
#include <GL/glx.h>
#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void check_gl_error(const char* operation) {
GLenum error = glGetError();
if (error != GL_NO_ERROR) {
printf("OpenGL error after %s: %d\n", operation, error);
}
}
int main(void) {
printf("=== Starting comprehensive OpenGL test ===\n");
// Open X11 display
printf("Opening X11 display...\n");
Display *dpy = XOpenDisplay(":99");
if (!dpy) {
fprintf(stderr, "ERROR: Cannot open X11 display :99\n");
return 1;
}
printf("✓ X11 display opened successfully\n");
// Check GLX extension
printf("Checking GLX extension...\n");
int glx_major, glx_minor;
if (!glXQueryVersion(dpy, &glx_major, &glx_minor)) {
fprintf(stderr, "ERROR: GLX extension not available\n");
XCloseDisplay(dpy);
return 1;
}
printf("✓ GLX version: %d.%d\n", glx_major, glx_minor);
// Choose visual
printf("Choosing GLX visual...\n");
static int attribs[] = {
GLX_RGBA,
GLX_DEPTH_SIZE, 16,
GLX_DOUBLEBUFFER,
None
};
XVisualInfo *vi = glXChooseVisual(dpy, DefaultScreen(dpy), attribs);
if (!vi) {
printf("WARNING: No double-buffered visual found, trying single-buffered...\n");
static int simple_attribs[] = { GLX_RGBA, GLX_DEPTH_SIZE, 16, None };
vi = glXChooseVisual(dpy, DefaultScreen(dpy), simple_attribs);
}
if (!vi) {
fprintf(stderr, "ERROR: No appropriate GLX visual found\n");
XCloseDisplay(dpy);
return 1;
}
printf("✓ GLX visual chosen (depth: %d, class: %d)\n", vi->depth, vi->class);
// Create GLX context
printf("Creating GLX context...\n");
GLXContext glc = glXCreateContext(dpy, vi, NULL, GL_TRUE);
if (!glc) {
fprintf(stderr, "ERROR: Failed to create GLX context\n");
XFree(vi);
XCloseDisplay(dpy);
return 1;
}
printf("✓ GLX context created successfully\n");
// Create window
printf("Creating X11 window...\n");
Window root = DefaultRootWindow(dpy);
XSetWindowAttributes swa;
swa.colormap = XCreateColormap(dpy, root, vi->visual, AllocNone);
swa.event_mask = ExposureMask | KeyPressMask;
swa.background_pixel = 0;
swa.border_pixel = 0;
Window win = XCreateWindow(dpy, root, 0, 0, 100, 100, 0,
vi->depth, InputOutput, vi->visual,
CWColormap | CWEventMask | CWBackPixel | CWBorderPixel, &swa);
if (!win) {
fprintf(stderr, "ERROR: Failed to create X11 window\n");
glXDestroyContext(dpy, glc);
XFree(vi);
XCloseDisplay(dpy);
return 1;
}
XMapWindow(dpy, win);
printf("✓ X11 window created and mapped\n");
// Make context current
printf("Making GLX context current...\n");
if (!glXMakeCurrent(dpy, win, glc)) {
fprintf(stderr, "ERROR: Failed to make GLX context current\n");
XDestroyWindow(dpy, win);
glXDestroyContext(dpy, glc);
XFree(vi);
XCloseDisplay(dpy);
return 1;
}
printf("✓ GLX context is now current\n");
// Test OpenGL calls
printf("Testing OpenGL calls...\n");
const GLubyte *vendor = glGetString(GL_VENDOR);
const GLubyte *renderer = glGetString(GL_RENDERER);
const GLubyte *version = glGetString(GL_VERSION);
const GLubyte *extensions = glGetString(GL_EXTENSIONS);
printf("✓ OpenGL Vendor: %s\n", vendor ? (char*)vendor : "NULL");
printf("✓ OpenGL Renderer: %s\n", renderer ? (char*)renderer : "NULL");
printf("✓ OpenGL Version: %s\n", version ? (char*)version : "NULL");
check_gl_error("glGetString calls");
// Test basic rendering
printf("Testing basic OpenGL rendering...\n");
glClearColor(0.2f, 0.3f, 0.4f, 1.0f);
check_gl_error("glClearColor");
glClear(GL_COLOR_BUFFER_BIT);
check_gl_error("glClear");
glXSwapBuffers(dpy, win);
printf("✓ Rendered and swapped buffers successfully\n");
// Test some basic OpenGL state
GLint max_texture_size;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
printf("✓ Max texture size: %d\n", max_texture_size);
check_gl_error("glGetIntegerv");
// Cleanup
printf("Cleaning up...\n");
glXMakeCurrent(dpy, None, NULL);
glXDestroyContext(dpy, glc);
XDestroyWindow(dpy, win);
XFree(vi);
XCloseDisplay(dpy);
printf("=== OpenGL test completed successfully! ===\n");
return 0;
}
EOF
echo "Compiling comprehensive OpenGL test..."
gcc comprehensive_gl_test.c -o comprehensive_gl_test -lGL -lX11 -lGLU
echo "Running comprehensive OpenGL test..."
./comprehensive_gl_test
- name: Test PUGL-style context creation (matching Floe requirements)
run: |
echo "=== Testing PUGL-style OpenGL context creation ==="
echo "Environment variables:"
echo "DISPLAY=$DISPLAY"
echo "LIBGL_ALWAYS_SOFTWARE=$LIBGL_ALWAYS_SOFTWARE"
echo "MESA_LOADER_DRIVER_OVERRIDE=$MESA_LOADER_DRIVER_OVERRIDE"
# Test that exactly matches PUGL's context creation approach
cat > pugl_style_test.c <<'EOF'
#include <GL/gl.h>
#include <GL/glx.h>
#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Function pointer for GLX_ARB_create_context
typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
int main(void) {
printf("=== Testing PUGL-style OpenGL context creation ===\n");
Display *display = XOpenDisplay(":99");
if (!display) {
fprintf(stderr, "ERROR: Cannot open display :99\n");
return 1;
}
printf("✓ Display opened\n");
int screen = DefaultScreen(display);
// PUGL-style FB config attributes (matching Floe's requirements)
const int attrs[] = {
GLX_X_RENDERABLE, True,
GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
GLX_RENDER_TYPE, GLX_RGBA_BIT,
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
GLX_ALPHA_SIZE, 8,
GLX_DEPTH_SIZE, 24,
GLX_STENCIL_SIZE, 8,
GLX_DOUBLEBUFFER, True,
None
};
printf("Choosing FB config...\n");
int n_fbc = 0;
GLXFBConfig* fbc = glXChooseFBConfig(display, screen, attrs, &n_fbc);
if (n_fbc <= 0 || !fbc) {
fprintf(stderr, "ERROR: No suitable FB config found\n");
XCloseDisplay(display);
return 1;
}
printf("✓ Found %d FB configs\n", n_fbc);
// Get visual info
XVisualInfo* vi = glXGetVisualFromFBConfig(display, fbc[0]);
if (!vi) {
fprintf(stderr, "ERROR: Could not get visual from FB config\n");
XFree(fbc);
XCloseDisplay(display);
return 1;
}
printf("✓ Got visual info\n");
// Check for GLX extensions that PUGL uses
const char* extensions = glXQueryExtensionsString(display, screen);
printf("Available GLX extensions:\n");
if (strstr(extensions, "GLX_ARB_create_context")) {
printf(" ✓ GLX_ARB_create_context\n");
} else {
printf(" ✗ GLX_ARB_create_context NOT AVAILABLE\n");
}
if (strstr(extensions, "GLX_EXT_swap_control")) {
printf(" ✓ GLX_EXT_swap_control\n");
} else {
printf(" ✗ GLX_EXT_swap_control\n");
}
GLXContext ctx = NULL;
// Try modern context creation (like PUGL does)
if (strstr(extensions, "GLX_ARB_create_context")) {
printf("Attempting modern context creation (OpenGL 3.3)...\n");
PFNGLXCREATECONTEXTATTRIBSARBPROC create_context =
(PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress(
(const unsigned char*)"glXCreateContextAttribsARB");
if (create_context) {
// Request OpenGL 3.3 Compatibility Profile (like Floe)
const int ctx_attrs[] = {
GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
GLX_CONTEXT_MINOR_VERSION_ARB, 3,
GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
None
};
ctx = create_context(display, fbc[0], 0, True, ctx_attrs);
if (ctx) {
printf("✓ Modern OpenGL 3.3 context created successfully!\n");
} else {
printf("✗ Modern context creation failed\n");
}
} else {
printf("✗ glXCreateContextAttribsARB function not found\n");
}
}
// Fallback to legacy context (like PUGL does)
if (!ctx) {
printf("Falling back to legacy context creation...\n");
ctx = glXCreateNewContext(display, fbc[0], GLX_RGBA_TYPE, 0, True);
if (ctx) {
printf("✓ Legacy context created successfully\n");
} else {
printf("✗ Legacy context creation also failed\n");
XFree(vi);
XFree(fbc);
XCloseDisplay(display);
return 1;
}
}
// Create a window
Window root = RootWindow(display, screen);
XSetWindowAttributes swa;
swa.colormap = XCreateColormap(display, root, vi->visual, AllocNone);
swa.event_mask = ExposureMask;
Window win = XCreateWindow(display, root, 0, 0, 100, 100, 0,
vi->depth, InputOutput, vi->visual,
CWColormap | CWEventMask, &swa);
XMapWindow(display, win);
// Make context current and test
if (!glXMakeCurrent(display, win, ctx)) {
fprintf(stderr, "ERROR: Failed to make context current\n");
return 1;
}
printf("✓ Context made current\n");
// Test OpenGL
printf("OpenGL Info:\n");
printf(" Vendor: %s\n", glGetString(GL_VENDOR));
printf(" Renderer: %s\n", glGetString(GL_RENDERER));
printf(" Version: %s\n", glGetString(GL_VERSION));
printf(" GLSL Version: %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
// Test some OpenGL operations
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
GLenum error = glGetError();
if (error != GL_NO_ERROR) {
printf("OpenGL error: %d\n", error);
} else {
printf("✓ Basic OpenGL operations successful\n");
}
// Cleanup
glXMakeCurrent(display, None, NULL);
glXDestroyContext(display, ctx);
XDestroyWindow(display, win);
XFree(vi);
XFree(fbc);
XCloseDisplay(display);
printf("=== PUGL-style test completed successfully! ===\n");
return 0;
}
EOF
gcc pugl_style_test.c -o pugl_style_test -lGL -lX11
./pugl_style_test
- name: Test different OpenGL context configurations
run: |
echo "=== Testing different context attribute combinations ==="
cat > context_variations_test.c <<'EOF'
#include <GL/gl.h>
#include <GL/glx.h>
#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void test_fb_config(Display* display, int screen, const char* name, const int* attrs) {
printf("\n--- Testing %s ---\n", name);
int n_fbc = 0;
GLXFBConfig* fbc = glXChooseFBConfig(display, screen, attrs, &n_fbc);
if (n_fbc <= 0 || !fbc) {
printf("✗ No FB configs found for %s\n", name);
return;
}
printf("✓ Found %d FB configs for %s\n", n_fbc, name);
// Try to get visual
XVisualInfo* vi = glXGetVisualFromFBConfig(display, fbc[0]);
if (!vi) {
printf("✗ Could not get visual for %s\n", name);
XFree(fbc);
return;
}
// Try legacy context creation
GLXContext ctx = glXCreateNewContext(display, fbc[0], GLX_RGBA_TYPE, 0, True);
if (ctx) {
printf("✓ Legacy context creation successful for %s\n", name);
glXDestroyContext(display, ctx);
} else {
printf("✗ Legacy context creation failed for %s\n", name);
}
XFree(vi);
XFree(fbc);
}
int main(void) {
Display *display = XOpenDisplay(":99");
if (!display) {
fprintf(stderr, "ERROR: Cannot open display\n");
return 1;
}
int screen = DefaultScreen(display);
// Test 1: Minimal config (should work)
const int minimal[] = {
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
GLX_RENDER_TYPE, GLX_RGBA_BIT,
None
};
test_fb_config(display, screen, "Minimal config", minimal);
// Test 2: Basic config without stencil
const int basic[] = {
GLX_X_RENDERABLE, True,
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
GLX_RENDER_TYPE, GLX_RGBA_BIT,
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
GLX_DEPTH_SIZE, 24,
GLX_DOUBLEBUFFER, True,
None
};
test_fb_config(display, screen, "Basic config (no stencil)", basic);
// Test 3: PUGL-style with smaller depth
const int pugl_small_depth[] = {
GLX_X_RENDERABLE, True,
GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
GLX_RENDER_TYPE, GLX_RGBA_BIT,
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
GLX_DEPTH_SIZE, 16, // Smaller depth
GLX_DOUBLEBUFFER, True,
None
};
test_fb_config(display, screen, "PUGL-style (16-bit depth)", pugl_small_depth);
// Test 4: Full PUGL config (what Floe actually requests)
const int pugl_full[] = {
GLX_X_RENDERABLE, True,
GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
GLX_RENDER_TYPE, GLX_RGBA_BIT,
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
GLX_ALPHA_SIZE, 8,
GLX_DEPTH_SIZE, 24,
GLX_STENCIL_SIZE, 8,
GLX_DOUBLEBUFFER, True,
None
};
test_fb_config(display, screen, "Full PUGL config (what Floe uses)", pugl_full);
// Test Mesa llvmpipe capabilities
printf("\n=== Mesa llvmpipe capabilities ===\n");
// Create a basic context to query capabilities
const int simple[] = { GLX_RGBA, None };
XVisualInfo* vi = glXChooseVisual(display, screen, (int*)simple);
if (vi) {
GLXContext ctx = glXCreateContext(display, vi, NULL, GL_TRUE);
if (ctx) {
Window root = RootWindow(display, screen);
XSetWindowAttributes swa;
swa.colormap = XCreateColormap(display, root, vi->visual, AllocNone);
Window win = XCreateWindow(display, root, 0, 0, 1, 1, 0,
vi->depth, InputOutput, vi->visual,
CWColormap, &swa);
if (glXMakeCurrent(display, win, ctx)) {
printf("OpenGL Limits:\n");
GLint max_texture_size;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
printf(" Max texture size: %d\n", max_texture_size);
GLint max_samples = 0;
glGetIntegerv(GL_MAX_SAMPLES, &max_samples);
GLenum error = glGetError();
if (error == GL_NO_ERROR && max_samples > 0) {
printf(" Max samples (MSAA): %d\n", max_samples);
} else {
printf(" MSAA not supported (error: %d)\n", error);
}
printf(" OpenGL version: %s\n", glGetString(GL_VERSION));
printf(" GLSL version: %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
}
glXDestroyContext(display, ctx);
XDestroyWindow(display, win);
}
XFree(vi);
}
XCloseDisplay(display);
return 0;
}
EOF
gcc context_variations_test.c -o context_variations_test -lGL -lX11
./context_variations_test
- name: Debug Mesa drivers and GLX info
run: |
echo "=== Mesa driver debugging ==="
# List available Mesa drivers
find /usr/lib -name "*dri*" -type f 2>/dev/null | head -10
# Check Mesa environment variables
echo "Mesa environment variables:"
env | grep -E "(MESA|LIBGL|GLX)" || echo "No Mesa environment variables set"
# Test GLX directly
echo "=== GLX capabilities ==="
glxinfo -display :99 | grep -A 20 "GLX version" || echo "glxinfo GLX check failed"
echo "=== GLX extensions ==="
glxinfo -display :99 | grep -A 10 "GLX extensions" || echo "glxinfo extensions check failed"
echo "=== Direct rendering status ==="
glxinfo -display :99 | grep "direct rendering" || echo "direct rendering check failed"
- name: Cleanup
if: always()
run: |
echo "=== Cleaning up ==="
# Kill Xvfb if it's still running
if [ -n "$XVFB_PID" ]; then
sudo kill $XVFB_PID 2>/dev/null || true
echo "Xvfb process $XVFB_PID terminated"
fi
# Clean up any remaining Xvfb processes
sudo pkill Xvfb 2>/dev/null || true
echo "Cleanup completed"
- name: Summary
if: always()
run: |
echo "=== Test Summary ==="
echo "This workflow tested:"
echo "1. ✓ Mesa package installation"
echo "2. ✓ Xvfb virtual display setup"
echo "3. ✓ OpenGL header compilation"
echo "4. ✓ GLX context creation and OpenGL rendering"
echo "5. ✓ Environment matching your main CI configuration"
echo ""
echo "If all steps passed, your OpenGL environment should work with Floe tests!"