Windows: automatically select the software renderer if OpenGL 2.0 is not available
Author: ancwrd1Created Jul 30, 2026Updated Sep 14, 2026
Labelsa:backend-winit
Feature Description
Rationale: in virtual environments, Windows does not always have OpenGL 2.0 drivers, but because Slint always selects femtovg as a default renderer, the application crashes. Basically, we have the following code in every application, platform-gated for Windows only, to do the manual selection based on OpenGL 2.0 availability.
pub fn init_gui_backend() -> anyhow::Result<()> {
if let Ok(backend) = env::var("SLINT_BACKEND") {
debug!("Using pre-selected rendering backend: {backend}");
slint::BackendSelector::new().select()?;
return Ok(());
}
let renderer = if has_opengl_support() { "femtovg" } else { "software" };
debug!("Using renderer: {renderer}");
if let Err(err) = slint::BackendSelector::new().renderer_name(renderer.into()).select() {
warn!("Failed to select rendering backend {renderer}: {err}; falling back to default");
slint::BackendSelector::new().select()?;
}
Ok(())
}
fn has_opengl_support() -> bool {
unsafe {
if LoadLibraryW(w!("opengl32.dll")).is_err() {
return false;
}
probe_gl_function()
}
}
unsafe fn probe_gl_function() -> bool {
unsafe {
let class_name = s!("SlintGLProbe");
let wc = WNDCLASSA {
lpfnWndProc: mem::transmute::<usize, WNDPROC>(DefWindowProcA as *const () as usize),
lpszClassName: class_name,
..Default::default()
};
RegisterClassA(&wc);
let hwnd = CreateWindowExA(
WINDOW_EX_STYLE(0),
class_name,
s!(""),
WS_OVERLAPPED,
0,
0,
1,
1,
Some(HWND::default()),
None,
None,
None,
);
let Ok(hwnd) = hwnd else { return false };
if hwnd.is_invalid() {
return false;
}
let hdc = GetDC(Some(hwnd));
let mut result = false;
let pfd = PIXELFORMATDESCRIPTOR {
nSize: mem::size_of::<PIXELFORMATDESCRIPTOR>() as u16,
nVersion: 1,
dwFlags: PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
iPixelType: PFD_TYPE_RGBA,
cColorBits: 32,
cDepthBits: 24,
iLayerType: PFD_MAIN_PLANE.0 as u8,
..Default::default()
};
let pixel_format = ChoosePixelFormat(hdc, &pfd);
if pixel_format != 0
&& SetPixelFormat(hdc, pixel_format, &pfd).is_ok()
&& let Ok(hglrc) = wglCreateContext(hdc)
{
if wglMakeCurrent(hdc, hglrc).is_ok() {
let ptr = wglGetProcAddress(s!("glCreateShader"));
// wglGetProcAddress returns 1/2/3/-1 as "not found" sentinels on
// some drivers in addition to NULL. Treat all of those as missing.
result = match ptr.map(|f| f as usize) {
None | Some(1 | 2 | 3 | usize::MAX) => false,
Some(_) => true,
};
let _ = wglMakeCurrent(hdc, HGLRC::default());
}
let _ = wglDeleteContext(hglrc);
}
ReleaseDC(Some(hwnd), hdc);
let _ = DestroyWindow(hwnd);
let _ = UnregisterClassA(class_name, None);
result
}
}
My suggestion is to add this to the library so that the backend selector takes Windows VMs into consideration.
Product Impact
No response
Source: slint-ui/slint