Description
In a Linux guest with GPU acceleration on, WebGL2 on the guest's GL driver (virgl) reports MAX_TEXTURE_IMAGE_UNITS = 32, but any fragment shader that uses more than 16 samplers fails on the host. The guest gets no GL error and no context loss: the draw silently produces nothing, and the kernel logs virtio_gpu errors. Programs that size their shaders from the reported limit therefore render nothing, and one that redraws every frame can leave its whole window blank.
Steps to Reproduce
- Create an Ubuntu 26.04 ARM64 VM with GPU acceleration enabled.
- In any Chromium-based browser or app in the guest, open DevTools and run the snippet below, changing the final
(17) to (16) to compare.
- Watch
sudo dmesg -w in the guest while it runs.
(N => {
const gl = document.createElement('canvas').getContext('webgl2');
const sh = (t, s) => { const x = gl.createShader(t); gl.shaderSource(x, s); gl.compileShader(x); return x; };
let chain = ''; for (let i = 1; i < N; i++) chain += ` else if (page == ${i}) o = texture(tex[${i}], vec2(0.5));`;
const p = gl.createProgram();
gl.attachShader(p, sh(gl.VERTEX_SHADER, `#version 300 es
layout(location=0) in vec2 a; void main(){ gl_Position = vec4(a, 0.0, 1.0); }`));
gl.attachShader(p, sh(gl.FRAGMENT_SHADER, `#version 300 es
precision lowp float; uniform int page; uniform sampler2D tex[${N}]; out vec4 o;
void main(){ if (page == 0) o = texture(tex[0], vec2(0.5));${chain} }`));
gl.linkProgram(p); gl.useProgram(p);
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,-1, 1,-1, -1,1, 1,1]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(0); gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
for (let i = 0; i < N; i++) {
gl.activeTexture(gl.TEXTURE0 + i); gl.bindTexture(gl.TEXTURE_2D, gl.createTexture());
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([255, 0, 0, 255]));
}
gl.uniform1iv(gl.getUniformLocation(p, 'tex'), Int32Array.from({length: N}, (_, i) => i));
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
const px = new Uint8Array(4); gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, px);
return {maxUnits: gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS), linked: gl.getProgramParameter(p, gl.LINK_STATUS),
glError: gl.getError(), lost: gl.isContextLost(), pixel: [...px]};
})(17)
Current Behavior
| Samplers in shader |
Pixel read back |
Guest GL error / context lost |
virtio_gpu kernel errors |
| 8 |
[255, 0, 0, 255] |
none / no |
0 |
| 16 |
[255, 0, 0, 255] |
none / no |
0 |
| 17 |
[0, 0, 0, 0] |
none / no |
5–6 |
| 32 |
[0, 0, 0, 0] |
none / no |
4–6 |
MAX_TEXTURE_IMAGE_UNITS is reported as 32 in every case, and the program links successfully in the guest. Each failing draw logs:
[drm:virtio_gpu_dequeue_ctrl_func [virtio_gpu]] *ERROR* response 0x1200 (command 0x207)
That is VIRTIO_GPU_RESP_ERR_UNSPEC in answer to VIRTIO_GPU_CMD_SUBMIT_3D.
Draws, instanced draws, texture uploads and mipmap generation with 16 or fewer samplers all work without errors.
Expected Behavior
Either the guest should report the limit the host can actually honour (16), or shaders using up to the reported 32 samplers should work. Failures should surface to the guest as a GL error or link failure rather than silently dropping the draw.
Environment
- App version: VMPal 0.67 (67)
- Mac: Apple M5 Pro
- macOS version: macOS 27.0.1 (26A434)
- VM's OS: Ubuntu 26.04.1 LTS (ARM64), kernel 7.0.0-38-generic, Mesa 26.0.8, GNOME on Wayland
- GL renderer in guest:
ANGLE (Mesa, virgl (Apple M5 Pro), OpenGL ES 3.0)
- Kernel:
virtio_gpu features +virgl +edid -resource_blob -host_visible -context_init
Log
No crash; the VM keeps running. Kernel message as above.
Description
In a Linux guest with GPU acceleration on, WebGL2 on the guest's GL driver (virgl) reports
MAX_TEXTURE_IMAGE_UNITS = 32, but any fragment shader that uses more than 16 samplers fails on the host. The guest gets no GL error and no context loss: the draw silently produces nothing, and the kernel logsvirtio_gpuerrors. Programs that size their shaders from the reported limit therefore render nothing, and one that redraws every frame can leave its whole window blank.Steps to Reproduce
(17)to(16)to compare.sudo dmesg -win the guest while it runs.Current Behavior
virtio_gpukernel errors[255, 0, 0, 255][255, 0, 0, 255][0, 0, 0, 0][0, 0, 0, 0]MAX_TEXTURE_IMAGE_UNITSis reported as 32 in every case, and the program links successfully in the guest. Each failing draw logs:That is
VIRTIO_GPU_RESP_ERR_UNSPECin answer toVIRTIO_GPU_CMD_SUBMIT_3D.Draws, instanced draws, texture uploads and mipmap generation with 16 or fewer samplers all work without errors.
Expected Behavior
Either the guest should report the limit the host can actually honour (16), or shaders using up to the reported 32 samplers should work. Failures should surface to the guest as a GL error or link failure rather than silently dropping the draw.
Environment
ANGLE (Mesa, virgl (Apple M5 Pro), OpenGL ES 3.0)virtio_gpufeatures+virgl +edid -resource_blob -host_visible -context_initLog
No crash; the VM keeps running. Kernel message as above.