The best way to run desktop Linux on Windows. With real GPU acceleration.
A modified QEMU with better WHPX support, Venus Vulkan rendering, and a Windows-native display driver. Because your VM deserves better.
CachyOS with KDE Plasma running SuperTuxKart, glmark2, vkmark, and vkcube — all inside WINQ-EMU on Windows.
Full -cpu host passthrough that upstream QEMU doesn't support on WHPX.
Hybrid CPU topology detection pins vCPUs to P-cores. Sub-millisecond timer resolution.
Your VM runs at native CPU speed.
Your Linux VM talks directly to your host GPU through the Venus protocol. Real hardware-accelerated Vulkan. No software rendering. No compromises. (Well, maybe a few compromises. It's alpha.)
DPI-aware SDL display with USB tablet support that doesn't freeze, automatic host refresh rate matching, and guest cursor forwarding. No grab deadlocks. No upscaling.
As a heavy Linux user, I have a great appreciation for how QEMU has allowed me to run a virtual machine which is just as capable as the host, with a fully graphically accelerated desktop. I wanted to bring the same Linux desktop experience to Windows, as I found Hyper-V, WSL, and QEMU in its upstream state to be insufficient. Hopefully the community will run with this and we'll end up with an even better Linux VM experience atop Windows.
(But honestly, you should probably just switch to Linux. QEMU is even better there.)
SuperTuxKart, Vulkan renderer, default settings. CachyOS on both. Intel B580 iGPU.
| Platform | FPS | |
|---|---|---|
| WINQ-EMU (Venus) | 410 fps | 👑 |
| WSL2 (CachyOS) | 226 fps |
Nearly 2x faster than WSL. Your mileage may vary. But it'll probably be good mileage.
Run the installer. Click next a few times. You know the drill.
Drop a Linux qcow2 image into the vm folder, or create one in the launcher.
Open WINQ-EMU, pick your settings, hit Launch. That's it.
The QEMU base is on the v11.0.0 tag, virglrenderer is on the 1.3.0 tag, and the Windows / Venus / WHPX patches sit on top as a clean series. But it's still alpha — things might crash, things might look weird, your cat might judge you. Pretty solid for something this experimental, but I'm covering my bases here.
Works everywhere — Wayland and X11. Just run your Vulkan apps. They'll find the GPU.
Works everywhere too. Your desktop compositor uses this by default. It just works™.
Translates GL to Vulkan for potentially better perf. Works on both native Wayland and X11/XWayland.
Set MESA_LOADER_DRIVER_OVERRIDE=zink to try it.
EFI boot causes a timing issue that tanks Vulkan to ~5 FPS. Stick with BIOS. I'm looking into it.
Linux uses the PAT MSR to mark virtio-gpu and Venus shared mappings as
Write-Combining. The previous WHPX path skipped syncing PAT in three places,
so the guest's cache-type computation could fall back to Uncacheable for
memory that should be WC. The fix is a 30-line change in
target/i386/whpx/whpx-all.c. Vulkan / OpenGL workloads that move
real GPU memory should be measurably faster on Alpha 10.
WHvAdviseGpaRange(Pin) now hints to the hypervisor that
anything 256 MiB or larger should keep its SLAT entries pinned — in
particular the base RAM and the 4 GiB Venus blob hostmem region. Before,
the hypervisor was free to demote 2 MiB / 1 GiB pages to 4 KiB
under host memory pressure, which produced a "60 fps → 30 fps and
I have no idea why" cliff under heavy graphics. The 24H2 partition properties
silently no-op on older Windows.
A guest spinning through cheap MMIO / CPUID / MSR exits during a Vulkan
submit storm could keep a vCPU thread inside WHvRunVirtualProcessor
for tens of ms, blocking the QEMU main loop and timer dispatch. Now we cap
time-in-loop at 5 ms wall clock, raise EXCP_INTERRUPT, and
yield. Smoother frame pacing without measurably hurting throughput.
FastHypercallOutput, UnimplementedMsrAction drop, faster bootThe ARM path has had FastHypercallOutput=1 for a while; x86
didn't. Now it does — every Hyper-V hypercall (TLB shootdown, IPI cluster)
returns via registers instead of a guest-physical output page. And on
24H2+ Windows, UnimplementedMsrAction = IgnoreWriteReadZero
tells the hypervisor to handle Linux's couple-hundred boot-time MSR probes
in-kernel instead of trapping to userspace.
The Intel ICD handle-recycle workaround in vkr_win32_shim.c already
covered 14 lightweight Vulkan object types; Alpha 10 adds
VkAccelerationStructureKHR. RT games (Quake II RTX, Portal RTX,
anything via vkd3d-proton) destroy and recreate AS handles every frame as TLAS
rebuilds, so this is the same bug class. If you have a working RT title that
previously crashed mid-run, please file an issue with a repro.
The Disk Image section is now a proper table: add as many qcow2 / raw
images as you want, choose virtio / scsi / ide per disk, and the first one
boots by default. Exported .bat files round-trip multiple
-drive lines so you can keep editing.
WINQ_DIAG=1 — one switch for diagnostic logsIf something looks wrong and I ask for a log, set WINQ_DIAG=1
in the env before launching. That flips on every existing per-module
diagnostic stream and routes the output to
%LOCALAPPDATA%\winq-emu\virglrenderer.log. Replaces the old matrix
of VIRGL_VIDEO_DIAG / VIRGL_VIDEO_D3D11_DEBUG /
VIRGL_LOG_FILE / VIRGL_LOG_LEVEL toggles.
NULL-resource deref guard in virgl_renderer_resource_map_fixed
(was guest-triggerable), vrend_renderer_init cleanup order on init
failure, vkr_allocator instance NULL on vkCreateInstance
failure, cursor Y_0_TOP orientation flip. Small reliability wins.
The host's Mesa-extension cleanup paths (vkWaitSemaphoreResourceMESA,
vkResetFenceResourceMESA) used to do synchronous GPU waits and Win32 handle
export/close ceremonies on every guest WSI present, blocking the dispatcher for the
GPU's frame time. They now match Linux's non-blocking semantics — the dispatcher
returns immediately and the GPU consumes the semaphore signal in the background. This
was a real per-frame stall on every Vulkan game and Wayland workload.
virgl_renderer_init now sets timeBeginPeriod(1) and the
ring relax path uses CreateWaitableTimerEx HIGH_RESOLUTION for sub-ms
delays. Without these, microsecond-scale dispatcher sleeps were rounded up to the
15.625 ms system timer tick — the dispatcher would oversleep into the next
frame and miss bursts of guest commands.
The ring monitor and per-queue sync threads register with the Multimedia Class Scheduler Service under "Pro Audio" so Windows treats them like low-latency audio workers: longer time slices, less likely to be preempted by background work. Reduces frame-time jitter on Vulkan workloads.
No QEMU changes vs Alpha 8 — same v11.0.0 base.
Alpha 8 rebases the QEMU fork on the QEMU 11.0.0 final release (up from the 11.0.0-rc3 snapshot used in Alpha 7) and the virglrenderer fork on the virglrenderer 1.3.0 stable tag (replacing the development-tip snapshot used in Alpha 7). The patch series now sits on tagged, reproducible upstream trees instead of release candidates and development tips. No functional changes vs Alpha 7 — same Windows-host Venus + virtio-gpu + 9pfs + D3D11 video decode feature set.
Zink (GL-over-Vulkan) now renders correctly under native Wayland compositors — not only X11/XWayland
as before. A Windows dma-buf shim in virglrenderer synthesizes
VK_EXT_external_memory_dma_buf and VK_EXT_image_drm_format_modifier
on top of the host Vulkan ICD so guest Wayland compositors can import Vulkan-allocated surfaces.
No user action needed — just set MESA_LOADER_DRIVER_OVERRIDE=zink in your guest
session if you want to force it.
Hardware video decode is moved to the launcher's new Experimental tab
and is off by default. When enabled, the guest's Mesa virgl VA driver forwards decode to the
Windows host GPU via DXVA. Supported: H.264 (Baseline / Main / High),
HEVC (Main, Main10), VP9 (Profile 0, Profile 2),
AV1 (Main Profile 0). Confirm with vainfo in the guest.
Works with: ffmpeg, mpv (--hwdec=vaapi-copy), VLC, Haruna, GStreamer
— anything that uses the vaGetImage / copy path.
Chrome / Brave / Edge / Opera / Vivaldi use the zero-copy vaExportSurfaceHandle
path, which is not yet functional through virglrenderer on Windows — expect black or garbled
video when hardware acceleration is on. Workaround: open
chrome://settings/system and uncheck "Use hardware acceleration when available",
or disable "hardware-video-decoding" in chrome://flags. mpv with the non-copy
--hwdec=vaapi option hits the same issue; use --hwdec=vaapi-copy instead.
Firefox and every non-Chromium consumer tested work correctly.
Windows 10 or 11 with "Windows Hypervisor Platform" enabled in Windows Features.
Any GPU with Vulkan drivers. Intel, AMD, NVIDIA — I don't discriminate.
A distro with Mesa 24.0+ (26.0+ for VA-API decode). Fedora 40+, Ubuntu 24.04+, Arch, CachyOS, etc.
This project takes a lot of time, testing, and hardware. If WINQ-EMU saved you from the horrors of software rendering or gave you a real Linux desktop on Windows, consider supporting the work. Donations help fund more hardware testing, upstream contributions, and the occasional mass of mass-produced tokens.
PayPal MeEvery bit helps. Even a star on GitHub makes a difference.
Everything is open source. The patches are intentionally minimal for easy upstreaming. If you want to merge any of this into the upstream projects, please do. I'd love that.