windows: Port to DirectX 11 (#34374)

张小白 , Kate , and Max Brunsfeld created

Closes #16713
Closes #19739
Closes #33191
Closes #26692
Closes #17374
Closes #35077
Closes https://github.com/zed-industries/zed/issues/35205
Closes https://github.com/zed-industries/zed/issues/35262


Compared to the current Vulkan implementation, this PR brings several
improvements:

- Fewer weird bugs
- Better hardware compatibility
- VSync support
- More accurate colors
- Lower memory usage
- Graceful handling of device loss

---

**TODO:**

- [x] Don’t use AGS binaries directly
- [ ] The message loop is using too much CPU when ths app is idle
- [x] There’s a
[bug](https://github.com/zed-industries/zed/issues/33191#issuecomment-3109306630)
in how `Path` is being rendered.

---

Release Notes:

- N/A

---------

Co-authored-by: Kate <kate@zed.dev>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>

Change summary

Cargo.toml                                           |    5 
crates/gpui/Cargo.toml                               |    5 
crates/gpui/build.rs                                 |  220 +
crates/gpui/src/platform.rs                          |    5 
crates/gpui/src/platform/linux/x11/client.rs         |    1 
crates/gpui/src/platform/windows.rs                  |    4 
crates/gpui/src/platform/windows/directx_atlas.rs    |  309 ++
crates/gpui/src/platform/windows/directx_renderer.rs | 1777 ++++++++++++++
crates/gpui/src/platform/windows/events.rs           |   94 
crates/gpui/src/platform/windows/platform.rs         |   46 
crates/gpui/src/platform/windows/shaders.hlsl        | 1160 +++++++++
crates/gpui/src/platform/windows/window.rs           |  107 
crates/gpui/src/window.rs                            |    2 
crates/zed/resources/windows/zed.iss                 |    1 
script/bundle-windows.ps1                            |   13 
tooling/workspace-hack/Cargo.toml                    |    2 
typos.toml                                           |    6 
17 files changed, 3,577 insertions(+), 180 deletions(-)

Detailed changes

Cargo.toml πŸ”—

@@ -680,8 +680,13 @@ features = [
     "Win32_Globalization",
     "Win32_Graphics_Direct2D",
     "Win32_Graphics_Direct2D_Common",
+    "Win32_Graphics_Direct3D",
+    "Win32_Graphics_Direct3D11",
+    "Win32_Graphics_Direct3D_Fxc",
+    "Win32_Graphics_DirectComposition",
     "Win32_Graphics_DirectWrite",
     "Win32_Graphics_Dwm",
+    "Win32_Graphics_Dxgi",
     "Win32_Graphics_Dxgi_Common",
     "Win32_Graphics_Gdi",
     "Win32_Graphics_Imaging",

crates/gpui/Cargo.toml πŸ”—

@@ -216,10 +216,6 @@ xim = { git = "https://github.com/XDeme1/xim-rs", rev = "d50d461764c2213655cd9cf
 x11-clipboard = { version = "0.9.3", optional = true }
 
 [target.'cfg(target_os = "windows")'.dependencies]
-blade-util.workspace = true
-bytemuck = "1"
-blade-graphics.workspace = true
-blade-macros.workspace = true
 flume = "0.11"
 rand.workspace = true
 windows.workspace = true
@@ -240,7 +236,6 @@ util = { workspace = true, features = ["test-support"] }
 
 [target.'cfg(target_os = "windows")'.build-dependencies]
 embed-resource = "3.0"
-naga.workspace = true
 
 [target.'cfg(target_os = "macos")'.build-dependencies]
 bindgen = "0.71"

crates/gpui/build.rs πŸ”—

@@ -9,7 +9,10 @@ fn main() {
     let target = env::var("CARGO_CFG_TARGET_OS");
     println!("cargo::rustc-check-cfg=cfg(gles)");
 
-    #[cfg(any(not(target_os = "macos"), feature = "macos-blade"))]
+    #[cfg(any(
+        not(any(target_os = "macos", target_os = "windows")),
+        all(target_os = "macos", feature = "macos-blade")
+    ))]
     check_wgsl_shaders();
 
     match target.as_deref() {
@@ -17,21 +20,18 @@ fn main() {
             #[cfg(target_os = "macos")]
             macos::build();
         }
-        #[cfg(all(target_os = "windows", feature = "windows-manifest"))]
         Ok("windows") => {
-            let manifest = std::path::Path::new("resources/windows/gpui.manifest.xml");
-            let rc_file = std::path::Path::new("resources/windows/gpui.rc");
-            println!("cargo:rerun-if-changed={}", manifest.display());
-            println!("cargo:rerun-if-changed={}", rc_file.display());
-            embed_resource::compile(rc_file, embed_resource::NONE)
-                .manifest_required()
-                .unwrap();
+            #[cfg(target_os = "windows")]
+            windows::build();
         }
         _ => (),
     };
 }
 
-#[allow(dead_code)]
+#[cfg(any(
+    not(any(target_os = "macos", target_os = "windows")),
+    all(target_os = "macos", feature = "macos-blade")
+))]
 fn check_wgsl_shaders() {
     use std::path::PathBuf;
     use std::process;
@@ -243,3 +243,203 @@ mod macos {
         }
     }
 }
+
+#[cfg(target_os = "windows")]
+mod windows {
+    use std::{
+        fs,
+        io::Write,
+        path::{Path, PathBuf},
+        process::{self, Command},
+    };
+
+    pub(super) fn build() {
+        // Compile HLSL shaders
+        #[cfg(not(debug_assertions))]
+        compile_shaders();
+
+        // Embed the Windows manifest and resource file
+        #[cfg(feature = "windows-manifest")]
+        embed_resource();
+    }
+
+    #[cfg(feature = "windows-manifest")]
+    fn embed_resource() {
+        let manifest = std::path::Path::new("resources/windows/gpui.manifest.xml");
+        let rc_file = std::path::Path::new("resources/windows/gpui.rc");
+        println!("cargo:rerun-if-changed={}", manifest.display());
+        println!("cargo:rerun-if-changed={}", rc_file.display());
+        embed_resource::compile(rc_file, embed_resource::NONE)
+            .manifest_required()
+            .unwrap();
+    }
+
+    /// You can set the `GPUI_FXC_PATH` environment variable to specify the path to the fxc.exe compiler.
+    fn compile_shaders() {
+        let shader_path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap())
+            .join("src/platform/windows/shaders.hlsl");
+        let out_dir = std::env::var("OUT_DIR").unwrap();
+
+        println!("cargo:rerun-if-changed={}", shader_path.display());
+
+        // Check if fxc.exe is available
+        let fxc_path = find_fxc_compiler();
+
+        // Define all modules
+        let modules = [
+            "quad",
+            "shadow",
+            "path_rasterization",
+            "path_sprite",
+            "underline",
+            "monochrome_sprite",
+            "polychrome_sprite",
+        ];
+
+        let rust_binding_path = format!("{}/shaders_bytes.rs", out_dir);
+        if Path::new(&rust_binding_path).exists() {
+            fs::remove_file(&rust_binding_path)
+                .expect("Failed to remove existing Rust binding file");
+        }
+        for module in modules {
+            compile_shader_for_module(
+                module,
+                &out_dir,
+                &fxc_path,
+                shader_path.to_str().unwrap(),
+                &rust_binding_path,
+            );
+        }
+    }
+
+    /// You can set the `GPUI_FXC_PATH` environment variable to specify the path to the fxc.exe compiler.
+    fn find_fxc_compiler() -> String {
+        // Check environment variable
+        if let Ok(path) = std::env::var("GPUI_FXC_PATH") {
+            if Path::new(&path).exists() {
+                return path;
+            }
+        }
+
+        // Try to find in PATH
+        // NOTE: This has to be `where.exe` on Windows, not `where`, it must be ended with `.exe`
+        if let Ok(output) = std::process::Command::new("where.exe")
+            .arg("fxc.exe")
+            .output()
+        {
+            if output.status.success() {
+                let path = String::from_utf8_lossy(&output.stdout);
+                return path.trim().to_string();
+            }
+        }
+
+        // Check the default path
+        if Path::new(r"C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\fxc.exe")
+            .exists()
+        {
+            return r"C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\fxc.exe"
+                .to_string();
+        }
+
+        panic!("Failed to find fxc.exe");
+    }
+
+    fn compile_shader_for_module(
+        module: &str,
+        out_dir: &str,
+        fxc_path: &str,
+        shader_path: &str,
+        rust_binding_path: &str,
+    ) {
+        // Compile vertex shader
+        let output_file = format!("{}/{}_vs.h", out_dir, module);
+        let const_name = format!("{}_VERTEX_BYTES", module.to_uppercase());
+        compile_shader_impl(
+            fxc_path,
+            &format!("{module}_vertex"),
+            &output_file,
+            &const_name,
+            shader_path,
+            "vs_4_1",
+        );
+        generate_rust_binding(&const_name, &output_file, &rust_binding_path);
+
+        // Compile fragment shader
+        let output_file = format!("{}/{}_ps.h", out_dir, module);
+        let const_name = format!("{}_FRAGMENT_BYTES", module.to_uppercase());
+        compile_shader_impl(
+            fxc_path,
+            &format!("{module}_fragment"),
+            &output_file,
+            &const_name,
+            shader_path,
+            "ps_4_1",
+        );
+        generate_rust_binding(&const_name, &output_file, &rust_binding_path);
+    }
+
+    fn compile_shader_impl(
+        fxc_path: &str,
+        entry_point: &str,
+        output_path: &str,
+        var_name: &str,
+        shader_path: &str,
+        target: &str,
+    ) {
+        let output = Command::new(fxc_path)
+            .args([
+                "/T",
+                target,
+                "/E",
+                entry_point,
+                "/Fh",
+                output_path,
+                "/Vn",
+                var_name,
+                "/O3",
+                shader_path,
+            ])
+            .output();
+
+        match output {
+            Ok(result) => {
+                if result.status.success() {
+                    return;
+                }
+                eprintln!(
+                    "Shader compilation failed for {}:\n{}",
+                    entry_point,
+                    String::from_utf8_lossy(&result.stderr)
+                );
+                process::exit(1);
+            }
+            Err(e) => {
+                eprintln!("Failed to run fxc for {}: {}", entry_point, e);
+                process::exit(1);
+            }
+        }
+    }
+
+    fn generate_rust_binding(const_name: &str, head_file: &str, output_path: &str) {
+        let header_content = fs::read_to_string(head_file).expect("Failed to read header file");
+        let const_definition = {
+            let global_var_start = header_content.find("const BYTE").unwrap();
+            let global_var = &header_content[global_var_start..];
+            let equal = global_var.find('=').unwrap();
+            global_var[equal + 1..].trim()
+        };
+        let rust_binding = format!(
+            "const {}: &[u8] = &{}\n",
+            const_name,
+            const_definition.replace('{', "[").replace('}', "]")
+        );
+        let mut options = fs::OpenOptions::new()
+            .create(true)
+            .append(true)
+            .open(output_path)
+            .expect("Failed to open Rust binding file");
+        options
+            .write_all(rust_binding.as_bytes())
+            .expect("Failed to write Rust binding file");
+    }
+}

crates/gpui/src/platform.rs πŸ”—

@@ -13,8 +13,7 @@ mod mac;
         any(target_os = "linux", target_os = "freebsd"),
         any(feature = "x11", feature = "wayland")
     ),
-    target_os = "windows",
-    feature = "macos-blade"
+    all(target_os = "macos", feature = "macos-blade")
 ))]
 mod blade;
 
@@ -448,6 +447,8 @@ impl Tiling {
 #[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
 pub(crate) struct RequestFrameOptions {
     pub(crate) require_presentation: bool,
+    /// Force refresh of all rendering states when true
+    pub(crate) force_render: bool,
 }
 
 pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle {

crates/gpui/src/platform/windows.rs πŸ”—

@@ -1,6 +1,8 @@
 mod clipboard;
 mod destination_list;
 mod direct_write;
+mod directx_atlas;
+mod directx_renderer;
 mod dispatcher;
 mod display;
 mod events;
@@ -14,6 +16,8 @@ mod wrapper;
 pub(crate) use clipboard::*;
 pub(crate) use destination_list::*;
 pub(crate) use direct_write::*;
+pub(crate) use directx_atlas::*;
+pub(crate) use directx_renderer::*;
 pub(crate) use dispatcher::*;
 pub(crate) use display::*;
 pub(crate) use events::*;

crates/gpui/src/platform/windows/directx_atlas.rs πŸ”—

@@ -0,0 +1,309 @@
+use collections::FxHashMap;
+use etagere::BucketedAtlasAllocator;
+use parking_lot::Mutex;
+use windows::Win32::Graphics::{
+    Direct3D11::{
+        D3D11_BIND_SHADER_RESOURCE, D3D11_BOX, D3D11_CPU_ACCESS_WRITE, D3D11_TEXTURE2D_DESC,
+        D3D11_USAGE_DEFAULT, ID3D11Device, ID3D11DeviceContext, ID3D11ShaderResourceView,
+        ID3D11Texture2D,
+    },
+    Dxgi::Common::{DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_SAMPLE_DESC},
+};
+
+use crate::{
+    AtlasKey, AtlasTextureId, AtlasTextureKind, AtlasTile, Bounds, DevicePixels, PlatformAtlas,
+    Point, Size, platform::AtlasTextureList,
+};
+
+pub(crate) struct DirectXAtlas(Mutex<DirectXAtlasState>);
+
+struct DirectXAtlasState {
+    device: ID3D11Device,
+    device_context: ID3D11DeviceContext,
+    monochrome_textures: AtlasTextureList<DirectXAtlasTexture>,
+    polychrome_textures: AtlasTextureList<DirectXAtlasTexture>,
+    tiles_by_key: FxHashMap<AtlasKey, AtlasTile>,
+}
+
+struct DirectXAtlasTexture {
+    id: AtlasTextureId,
+    bytes_per_pixel: u32,
+    allocator: BucketedAtlasAllocator,
+    texture: ID3D11Texture2D,
+    view: [Option<ID3D11ShaderResourceView>; 1],
+    live_atlas_keys: u32,
+}
+
+impl DirectXAtlas {
+    pub(crate) fn new(device: &ID3D11Device, device_context: &ID3D11DeviceContext) -> Self {
+        DirectXAtlas(Mutex::new(DirectXAtlasState {
+            device: device.clone(),
+            device_context: device_context.clone(),
+            monochrome_textures: Default::default(),
+            polychrome_textures: Default::default(),
+            tiles_by_key: Default::default(),
+        }))
+    }
+
+    pub(crate) fn get_texture_view(
+        &self,
+        id: AtlasTextureId,
+    ) -> [Option<ID3D11ShaderResourceView>; 1] {
+        let lock = self.0.lock();
+        let tex = lock.texture(id);
+        tex.view.clone()
+    }
+
+    pub(crate) fn handle_device_lost(
+        &self,
+        device: &ID3D11Device,
+        device_context: &ID3D11DeviceContext,
+    ) {
+        let mut lock = self.0.lock();
+        lock.device = device.clone();
+        lock.device_context = device_context.clone();
+        lock.monochrome_textures = AtlasTextureList::default();
+        lock.polychrome_textures = AtlasTextureList::default();
+        lock.tiles_by_key.clear();
+    }
+}
+
+impl PlatformAtlas for DirectXAtlas {
+    fn get_or_insert_with<'a>(
+        &self,
+        key: &AtlasKey,
+        build: &mut dyn FnMut() -> anyhow::Result<
+            Option<(Size<DevicePixels>, std::borrow::Cow<'a, [u8]>)>,
+        >,
+    ) -> anyhow::Result<Option<AtlasTile>> {
+        let mut lock = self.0.lock();
+        if let Some(tile) = lock.tiles_by_key.get(key) {
+            Ok(Some(tile.clone()))
+        } else {
+            let Some((size, bytes)) = build()? else {
+                return Ok(None);
+            };
+            let tile = lock
+                .allocate(size, key.texture_kind())
+                .ok_or_else(|| anyhow::anyhow!("failed to allocate"))?;
+            let texture = lock.texture(tile.texture_id);
+            texture.upload(&lock.device_context, tile.bounds, &bytes);
+            lock.tiles_by_key.insert(key.clone(), tile.clone());
+            Ok(Some(tile))
+        }
+    }
+
+    fn remove(&self, key: &AtlasKey) {
+        let mut lock = self.0.lock();
+
+        let Some(id) = lock.tiles_by_key.remove(key).map(|tile| tile.texture_id) else {
+            return;
+        };
+
+        let textures = match id.kind {
+            AtlasTextureKind::Monochrome => &mut lock.monochrome_textures,
+            AtlasTextureKind::Polychrome => &mut lock.polychrome_textures,
+        };
+
+        let Some(texture_slot) = textures.textures.get_mut(id.index as usize) else {
+            return;
+        };
+
+        if let Some(mut texture) = texture_slot.take() {
+            texture.decrement_ref_count();
+            if texture.is_unreferenced() {
+                textures.free_list.push(texture.id.index as usize);
+                lock.tiles_by_key.remove(key);
+            } else {
+                *texture_slot = Some(texture);
+            }
+        }
+    }
+}
+
+impl DirectXAtlasState {
+    fn allocate(
+        &mut self,
+        size: Size<DevicePixels>,
+        texture_kind: AtlasTextureKind,
+    ) -> Option<AtlasTile> {
+        {
+            let textures = match texture_kind {
+                AtlasTextureKind::Monochrome => &mut self.monochrome_textures,
+                AtlasTextureKind::Polychrome => &mut self.polychrome_textures,
+            };
+
+            if let Some(tile) = textures
+                .iter_mut()
+                .rev()
+                .find_map(|texture| texture.allocate(size))
+            {
+                return Some(tile);
+            }
+        }
+
+        let texture = self.push_texture(size, texture_kind)?;
+        texture.allocate(size)
+    }
+
+    fn push_texture(
+        &mut self,
+        min_size: Size<DevicePixels>,
+        kind: AtlasTextureKind,
+    ) -> Option<&mut DirectXAtlasTexture> {
+        const DEFAULT_ATLAS_SIZE: Size<DevicePixels> = Size {
+            width: DevicePixels(1024),
+            height: DevicePixels(1024),
+        };
+        // Max texture size for DirectX. See:
+        // https://learn.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-limits
+        const MAX_ATLAS_SIZE: Size<DevicePixels> = Size {
+            width: DevicePixels(16384),
+            height: DevicePixels(16384),
+        };
+        let size = min_size.min(&MAX_ATLAS_SIZE).max(&DEFAULT_ATLAS_SIZE);
+        let pixel_format;
+        let bind_flag;
+        let bytes_per_pixel;
+        match kind {
+            AtlasTextureKind::Monochrome => {
+                pixel_format = DXGI_FORMAT_A8_UNORM;
+                bind_flag = D3D11_BIND_SHADER_RESOURCE;
+                bytes_per_pixel = 1;
+            }
+            AtlasTextureKind::Polychrome => {
+                pixel_format = DXGI_FORMAT_B8G8R8A8_UNORM;
+                bind_flag = D3D11_BIND_SHADER_RESOURCE;
+                bytes_per_pixel = 4;
+            }
+        }
+        let texture_desc = D3D11_TEXTURE2D_DESC {
+            Width: size.width.0 as u32,
+            Height: size.height.0 as u32,
+            MipLevels: 1,
+            ArraySize: 1,
+            Format: pixel_format,
+            SampleDesc: DXGI_SAMPLE_DESC {
+                Count: 1,
+                Quality: 0,
+            },
+            Usage: D3D11_USAGE_DEFAULT,
+            BindFlags: bind_flag.0 as u32,
+            CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
+            MiscFlags: 0,
+        };
+        let mut texture: Option<ID3D11Texture2D> = None;
+        unsafe {
+            // This only returns None if the device is lost, which we will recreate later.
+            // So it's ok to return None here.
+            self.device
+                .CreateTexture2D(&texture_desc, None, Some(&mut texture))
+                .ok()?;
+        }
+        let texture = texture.unwrap();
+
+        let texture_list = match kind {
+            AtlasTextureKind::Monochrome => &mut self.monochrome_textures,
+            AtlasTextureKind::Polychrome => &mut self.polychrome_textures,
+        };
+        let index = texture_list.free_list.pop();
+        let view = unsafe {
+            let mut view = None;
+            self.device
+                .CreateShaderResourceView(&texture, None, Some(&mut view))
+                .ok()?;
+            [view]
+        };
+        let atlas_texture = DirectXAtlasTexture {
+            id: AtlasTextureId {
+                index: index.unwrap_or(texture_list.textures.len()) as u32,
+                kind,
+            },
+            bytes_per_pixel,
+            allocator: etagere::BucketedAtlasAllocator::new(size.into()),
+            texture,
+            view,
+            live_atlas_keys: 0,
+        };
+        if let Some(ix) = index {
+            texture_list.textures[ix] = Some(atlas_texture);
+            texture_list.textures.get_mut(ix).unwrap().as_mut()
+        } else {
+            texture_list.textures.push(Some(atlas_texture));
+            texture_list.textures.last_mut().unwrap().as_mut()
+        }
+    }
+
+    fn texture(&self, id: AtlasTextureId) -> &DirectXAtlasTexture {
+        let textures = match id.kind {
+            crate::AtlasTextureKind::Monochrome => &self.monochrome_textures,
+            crate::AtlasTextureKind::Polychrome => &self.polychrome_textures,
+        };
+        textures[id.index as usize].as_ref().unwrap()
+    }
+}
+
+impl DirectXAtlasTexture {
+    fn allocate(&mut self, size: Size<DevicePixels>) -> Option<AtlasTile> {
+        let allocation = self.allocator.allocate(size.into())?;
+        let tile = AtlasTile {
+            texture_id: self.id,
+            tile_id: allocation.id.into(),
+            bounds: Bounds {
+                origin: allocation.rectangle.min.into(),
+                size,
+            },
+            padding: 0,
+        };
+        self.live_atlas_keys += 1;
+        Some(tile)
+    }
+
+    fn upload(
+        &self,
+        device_context: &ID3D11DeviceContext,
+        bounds: Bounds<DevicePixels>,
+        bytes: &[u8],
+    ) {
+        unsafe {
+            device_context.UpdateSubresource(
+                &self.texture,
+                0,
+                Some(&D3D11_BOX {
+                    left: bounds.left().0 as u32,
+                    top: bounds.top().0 as u32,
+                    front: 0,
+                    right: bounds.right().0 as u32,
+                    bottom: bounds.bottom().0 as u32,
+                    back: 1,
+                }),
+                bytes.as_ptr() as _,
+                bounds.size.width.to_bytes(self.bytes_per_pixel as u8),
+                0,
+            );
+        }
+    }
+
+    fn decrement_ref_count(&mut self) {
+        self.live_atlas_keys -= 1;
+    }
+
+    fn is_unreferenced(&mut self) -> bool {
+        self.live_atlas_keys == 0
+    }
+}
+
+impl From<Size<DevicePixels>> for etagere::Size {
+    fn from(size: Size<DevicePixels>) -> Self {
+        etagere::Size::new(size.width.into(), size.height.into())
+    }
+}
+
+impl From<etagere::Point> for Point<DevicePixels> {
+    fn from(value: etagere::Point) -> Self {
+        Point {
+            x: DevicePixels::from(value.x),
+            y: DevicePixels::from(value.y),
+        }
+    }
+}

crates/gpui/src/platform/windows/directx_renderer.rs πŸ”—

@@ -0,0 +1,1777 @@
+use std::{mem::ManuallyDrop, sync::Arc};
+
+use ::util::ResultExt;
+use anyhow::{Context, Result};
+use windows::{
+    Win32::{
+        Foundation::{HMODULE, HWND},
+        Graphics::{
+            Direct3D::*,
+            Direct3D11::*,
+            DirectComposition::*,
+            Dxgi::{Common::*, *},
+        },
+    },
+    core::Interface,
+};
+
+use crate::{
+    platform::windows::directx_renderer::shader_resources::{
+        RawShaderBytes, ShaderModule, ShaderTarget,
+    },
+    *,
+};
+
+pub(crate) const DISABLE_DIRECT_COMPOSITION: &str = "GPUI_DISABLE_DIRECT_COMPOSITION";
+const RENDER_TARGET_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM;
+// This configuration is used for MSAA rendering on paths only, and it's guaranteed to be supported by DirectX 11.
+const PATH_MULTISAMPLE_COUNT: u32 = 4;
+
+pub(crate) struct DirectXRenderer {
+    hwnd: HWND,
+    atlas: Arc<DirectXAtlas>,
+    devices: ManuallyDrop<DirectXDevices>,
+    resources: ManuallyDrop<DirectXResources>,
+    globals: DirectXGlobalElements,
+    pipelines: DirectXRenderPipelines,
+    direct_composition: Option<DirectComposition>,
+}
+
+/// Direct3D objects
+#[derive(Clone)]
+pub(crate) struct DirectXDevices {
+    adapter: IDXGIAdapter1,
+    dxgi_factory: IDXGIFactory6,
+    device: ID3D11Device,
+    device_context: ID3D11DeviceContext,
+    dxgi_device: Option<IDXGIDevice>,
+}
+
+struct DirectXResources {
+    // Direct3D rendering objects
+    swap_chain: IDXGISwapChain1,
+    render_target: ManuallyDrop<ID3D11Texture2D>,
+    render_target_view: [Option<ID3D11RenderTargetView>; 1],
+
+    // Path intermediate textures (with MSAA)
+    path_intermediate_texture: ID3D11Texture2D,
+    path_intermediate_srv: [Option<ID3D11ShaderResourceView>; 1],
+    path_intermediate_msaa_texture: ID3D11Texture2D,
+    path_intermediate_msaa_view: [Option<ID3D11RenderTargetView>; 1],
+
+    // Cached window size and viewport
+    width: u32,
+    height: u32,
+    viewport: [D3D11_VIEWPORT; 1],
+}
+
+struct DirectXRenderPipelines {
+    shadow_pipeline: PipelineState<Shadow>,
+    quad_pipeline: PipelineState<Quad>,
+    path_rasterization_pipeline: PipelineState<PathRasterizationSprite>,
+    path_sprite_pipeline: PipelineState<PathSprite>,
+    underline_pipeline: PipelineState<Underline>,
+    mono_sprites: PipelineState<MonochromeSprite>,
+    poly_sprites: PipelineState<PolychromeSprite>,
+}
+
+struct DirectXGlobalElements {
+    global_params_buffer: [Option<ID3D11Buffer>; 1],
+    sampler: [Option<ID3D11SamplerState>; 1],
+}
+
+struct DirectComposition {
+    comp_device: IDCompositionDevice,
+    comp_target: IDCompositionTarget,
+    comp_visual: IDCompositionVisual,
+}
+
+impl DirectXDevices {
+    pub(crate) fn new(disable_direct_composition: bool) -> Result<ManuallyDrop<Self>> {
+        let dxgi_factory = get_dxgi_factory().context("Creating DXGI factory")?;
+        let adapter = get_adapter(&dxgi_factory).context("Getting DXGI adapter")?;
+        let (device, device_context) = {
+            let mut device: Option<ID3D11Device> = None;
+            let mut context: Option<ID3D11DeviceContext> = None;
+            let mut feature_level = D3D_FEATURE_LEVEL::default();
+            get_device(
+                &adapter,
+                Some(&mut device),
+                Some(&mut context),
+                Some(&mut feature_level),
+            )
+            .context("Creating Direct3D device")?;
+            match feature_level {
+                D3D_FEATURE_LEVEL_11_1 => {
+                    log::info!("Created device with Direct3D 11.1 feature level.")
+                }
+                D3D_FEATURE_LEVEL_11_0 => {
+                    log::info!("Created device with Direct3D 11.0 feature level.")
+                }
+                D3D_FEATURE_LEVEL_10_1 => {
+                    log::info!("Created device with Direct3D 10.1 feature level.")
+                }
+                _ => unreachable!(),
+            }
+            (device.unwrap(), context.unwrap())
+        };
+        let dxgi_device = if disable_direct_composition {
+            None
+        } else {
+            Some(device.cast().context("Creating DXGI device")?)
+        };
+
+        Ok(ManuallyDrop::new(Self {
+            adapter,
+            dxgi_factory,
+            dxgi_device,
+            device,
+            device_context,
+        }))
+    }
+}
+
+impl DirectXRenderer {
+    pub(crate) fn new(hwnd: HWND, disable_direct_composition: bool) -> Result<Self> {
+        if disable_direct_composition {
+            log::info!("Direct Composition is disabled.");
+        }
+
+        let devices =
+            DirectXDevices::new(disable_direct_composition).context("Creating DirectX devices")?;
+        let atlas = Arc::new(DirectXAtlas::new(&devices.device, &devices.device_context));
+
+        let resources = DirectXResources::new(&devices, 1, 1, hwnd, disable_direct_composition)
+            .context("Creating DirectX resources")?;
+        let globals = DirectXGlobalElements::new(&devices.device)
+            .context("Creating DirectX global elements")?;
+        let pipelines = DirectXRenderPipelines::new(&devices.device)
+            .context("Creating DirectX render pipelines")?;
+
+        let direct_composition = if disable_direct_composition {
+            None
+        } else {
+            let composition = DirectComposition::new(devices.dxgi_device.as_ref().unwrap(), hwnd)
+                .context("Creating DirectComposition")?;
+            composition
+                .set_swap_chain(&resources.swap_chain)
+                .context("Setting swap chain for DirectComposition")?;
+            Some(composition)
+        };
+
+        Ok(DirectXRenderer {
+            hwnd,
+            atlas,
+            devices,
+            resources,
+            globals,
+            pipelines,
+            direct_composition,
+        })
+    }
+
+    pub(crate) fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
+        self.atlas.clone()
+    }
+
+    fn pre_draw(&self) -> Result<()> {
+        update_buffer(
+            &self.devices.device_context,
+            self.globals.global_params_buffer[0].as_ref().unwrap(),
+            &[GlobalParams {
+                viewport_size: [
+                    self.resources.viewport[0].Width,
+                    self.resources.viewport[0].Height,
+                ],
+                ..Default::default()
+            }],
+        )?;
+        unsafe {
+            self.devices.device_context.ClearRenderTargetView(
+                self.resources.render_target_view[0].as_ref().unwrap(),
+                &[0.0; 4],
+            );
+            self.devices
+                .device_context
+                .OMSetRenderTargets(Some(&self.resources.render_target_view), None);
+            self.devices
+                .device_context
+                .RSSetViewports(Some(&self.resources.viewport));
+        }
+        Ok(())
+    }
+
+    fn present(&mut self) -> Result<()> {
+        unsafe {
+            let result = self.resources.swap_chain.Present(1, DXGI_PRESENT(0));
+            // Presenting the swap chain can fail if the DirectX device was removed or reset.
+            if result == DXGI_ERROR_DEVICE_REMOVED || result == DXGI_ERROR_DEVICE_RESET {
+                let reason = self.devices.device.GetDeviceRemovedReason();
+                log::error!(
+                    "DirectX device removed or reset when drawing. Reason: {:?}",
+                    reason
+                );
+                self.handle_device_lost()?;
+            } else {
+                result.ok()?;
+            }
+        }
+        Ok(())
+    }
+
+    fn handle_device_lost(&mut self) -> Result<()> {
+        // Here we wait a bit to ensure the the system has time to recover from the device lost state.
+        // If we don't wait, the final drawing result will be blank.
+        std::thread::sleep(std::time::Duration::from_millis(300));
+        let disable_direct_composition = self.direct_composition.is_none();
+
+        unsafe {
+            #[cfg(debug_assertions)]
+            report_live_objects(&self.devices.device)
+                .context("Failed to report live objects after device lost")
+                .log_err();
+
+            ManuallyDrop::drop(&mut self.resources);
+            self.devices.device_context.OMSetRenderTargets(None, None);
+            self.devices.device_context.ClearState();
+            self.devices.device_context.Flush();
+
+            #[cfg(debug_assertions)]
+            report_live_objects(&self.devices.device)
+                .context("Failed to report live objects after device lost")
+                .log_err();
+
+            drop(self.direct_composition.take());
+            ManuallyDrop::drop(&mut self.devices);
+        }
+
+        let devices = DirectXDevices::new(disable_direct_composition)
+            .context("Recreating DirectX devices")?;
+        let resources = DirectXResources::new(
+            &devices,
+            self.resources.width,
+            self.resources.height,
+            self.hwnd,
+            disable_direct_composition,
+        )?;
+        let globals = DirectXGlobalElements::new(&devices.device)?;
+        let pipelines = DirectXRenderPipelines::new(&devices.device)?;
+
+        let direct_composition = if disable_direct_composition {
+            None
+        } else {
+            let composition =
+                DirectComposition::new(devices.dxgi_device.as_ref().unwrap(), self.hwnd)?;
+            composition.set_swap_chain(&resources.swap_chain)?;
+            Some(composition)
+        };
+
+        self.atlas
+            .handle_device_lost(&devices.device, &devices.device_context);
+        self.devices = devices;
+        self.resources = resources;
+        self.globals = globals;
+        self.pipelines = pipelines;
+        self.direct_composition = direct_composition;
+
+        unsafe {
+            self.devices
+                .device_context
+                .OMSetRenderTargets(Some(&self.resources.render_target_view), None);
+        }
+        Ok(())
+    }
+
+    pub(crate) fn draw(&mut self, scene: &Scene) -> Result<()> {
+        self.pre_draw()?;
+        for batch in scene.batches() {
+            match batch {
+                PrimitiveBatch::Shadows(shadows) => self.draw_shadows(shadows),
+                PrimitiveBatch::Quads(quads) => self.draw_quads(quads),
+                PrimitiveBatch::Paths(paths) => {
+                    self.draw_paths_to_intermediate(paths)?;
+                    self.draw_paths_from_intermediate(paths)
+                }
+                PrimitiveBatch::Underlines(underlines) => self.draw_underlines(underlines),
+                PrimitiveBatch::MonochromeSprites {
+                    texture_id,
+                    sprites,
+                } => self.draw_monochrome_sprites(texture_id, sprites),
+                PrimitiveBatch::PolychromeSprites {
+                    texture_id,
+                    sprites,
+                } => self.draw_polychrome_sprites(texture_id, sprites),
+                PrimitiveBatch::Surfaces(surfaces) => self.draw_surfaces(surfaces),
+            }.context(format!("scene too large: {} paths, {} shadows, {} quads, {} underlines, {} mono, {} poly, {} surfaces",
+                    scene.paths.len(),
+                    scene.shadows.len(),
+                    scene.quads.len(),
+                    scene.underlines.len(),
+                    scene.monochrome_sprites.len(),
+                    scene.polychrome_sprites.len(),
+                    scene.surfaces.len(),))?;
+        }
+        self.present()
+    }
+
+    pub(crate) fn resize(&mut self, new_size: Size<DevicePixels>) -> Result<()> {
+        let width = new_size.width.0.max(1) as u32;
+        let height = new_size.height.0.max(1) as u32;
+        if self.resources.width == width && self.resources.height == height {
+            return Ok(());
+        }
+        unsafe {
+            // Clear the render target before resizing
+            self.devices.device_context.OMSetRenderTargets(None, None);
+            ManuallyDrop::drop(&mut self.resources.render_target);
+            drop(self.resources.render_target_view[0].take().unwrap());
+
+            let result = self.resources.swap_chain.ResizeBuffers(
+                BUFFER_COUNT as u32,
+                width,
+                height,
+                RENDER_TARGET_FORMAT,
+                DXGI_SWAP_CHAIN_FLAG(0),
+            );
+            // Resizing the swap chain requires a call to the underlying DXGI adapter, which can return the device removed error.
+            // The app might have moved to a monitor that's attached to a different graphics device.
+            // When a graphics device is removed or reset, the desktop resolution often changes, resulting in a window size change.
+            match result {
+                Ok(_) => {}
+                Err(e) => {
+                    if e.code() == DXGI_ERROR_DEVICE_REMOVED || e.code() == DXGI_ERROR_DEVICE_RESET
+                    {
+                        let reason = self.devices.device.GetDeviceRemovedReason();
+                        log::error!(
+                            "DirectX device removed or reset when resizing. Reason: {:?}",
+                            reason
+                        );
+                        self.resources.width = width;
+                        self.resources.height = height;
+                        self.handle_device_lost()?;
+                        return Ok(());
+                    } else {
+                        log::error!("Failed to resize swap chain: {:?}", e);
+                        return Err(e.into());
+                    }
+                }
+            }
+
+            self.resources
+                .recreate_resources(&self.devices, width, height)?;
+            self.devices
+                .device_context
+                .OMSetRenderTargets(Some(&self.resources.render_target_view), None);
+        }
+        Ok(())
+    }
+
+    fn draw_shadows(&mut self, shadows: &[Shadow]) -> Result<()> {
+        if shadows.is_empty() {
+            return Ok(());
+        }
+        self.pipelines.shadow_pipeline.update_buffer(
+            &self.devices.device,
+            &self.devices.device_context,
+            shadows,
+        )?;
+        self.pipelines.shadow_pipeline.draw(
+            &self.devices.device_context,
+            &self.resources.viewport,
+            &self.globals.global_params_buffer,
+            D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
+            4,
+            shadows.len() as u32,
+        )
+    }
+
+    fn draw_quads(&mut self, quads: &[Quad]) -> Result<()> {
+        if quads.is_empty() {
+            return Ok(());
+        }
+        self.pipelines.quad_pipeline.update_buffer(
+            &self.devices.device,
+            &self.devices.device_context,
+            quads,
+        )?;
+        self.pipelines.quad_pipeline.draw(
+            &self.devices.device_context,
+            &self.resources.viewport,
+            &self.globals.global_params_buffer,
+            D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
+            4,
+            quads.len() as u32,
+        )
+    }
+
+    fn draw_paths_to_intermediate(&mut self, paths: &[Path<ScaledPixels>]) -> Result<()> {
+        if paths.is_empty() {
+            return Ok(());
+        }
+
+        // Clear intermediate MSAA texture
+        unsafe {
+            self.devices.device_context.ClearRenderTargetView(
+                self.resources.path_intermediate_msaa_view[0]
+                    .as_ref()
+                    .unwrap(),
+                &[0.0; 4],
+            );
+            // Set intermediate MSAA texture as render target
+            self.devices
+                .device_context
+                .OMSetRenderTargets(Some(&self.resources.path_intermediate_msaa_view), None);
+        }
+
+        // Collect all vertices and sprites for a single draw call
+        let mut vertices = Vec::new();
+
+        for path in paths {
+            vertices.extend(path.vertices.iter().map(|v| PathRasterizationSprite {
+                xy_position: v.xy_position,
+                st_position: v.st_position,
+                color: path.color,
+                bounds: path.bounds.intersect(&path.content_mask.bounds),
+            }));
+        }
+
+        self.pipelines.path_rasterization_pipeline.update_buffer(
+            &self.devices.device,
+            &self.devices.device_context,
+            &vertices,
+        )?;
+        self.pipelines.path_rasterization_pipeline.draw(
+            &self.devices.device_context,
+            &self.resources.viewport,
+            &self.globals.global_params_buffer,
+            D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
+            vertices.len() as u32,
+            1,
+        )?;
+
+        // Resolve MSAA to non-MSAA intermediate texture
+        unsafe {
+            self.devices.device_context.ResolveSubresource(
+                &self.resources.path_intermediate_texture,
+                0,
+                &self.resources.path_intermediate_msaa_texture,
+                0,
+                RENDER_TARGET_FORMAT,
+            );
+            // Restore main render target
+            self.devices
+                .device_context
+                .OMSetRenderTargets(Some(&self.resources.render_target_view), None);
+        }
+
+        Ok(())
+    }
+
+    fn draw_paths_from_intermediate(&mut self, paths: &[Path<ScaledPixels>]) -> Result<()> {
+        let Some(first_path) = paths.first() else {
+            return Ok(());
+        };
+
+        // When copying paths from the intermediate texture to the drawable,
+        // each pixel must only be copied once, in case of transparent paths.
+        //
+        // If all paths have the same draw order, then their bounds are all
+        // disjoint, so we can copy each path's bounds individually. If this
+        // batch combines different draw orders, we perform a single copy
+        // for a minimal spanning rect.
+        let sprites = if paths.last().unwrap().order == first_path.order {
+            paths
+                .iter()
+                .map(|path| PathSprite {
+                    bounds: path.bounds,
+                })
+                .collect::<Vec<_>>()
+        } else {
+            let mut bounds = first_path.bounds;
+            for path in paths.iter().skip(1) {
+                bounds = bounds.union(&path.bounds);
+            }
+            vec![PathSprite { bounds }]
+        };
+
+        self.pipelines.path_sprite_pipeline.update_buffer(
+            &self.devices.device,
+            &self.devices.device_context,
+            &sprites,
+        )?;
+
+        // Draw the sprites with the path texture
+        self.pipelines.path_sprite_pipeline.draw_with_texture(
+            &self.devices.device_context,
+            &self.resources.path_intermediate_srv,
+            &self.resources.viewport,
+            &self.globals.global_params_buffer,
+            &self.globals.sampler,
+            sprites.len() as u32,
+        )
+    }
+
+    fn draw_underlines(&mut self, underlines: &[Underline]) -> Result<()> {
+        if underlines.is_empty() {
+            return Ok(());
+        }
+        self.pipelines.underline_pipeline.update_buffer(
+            &self.devices.device,
+            &self.devices.device_context,
+            underlines,
+        )?;
+        self.pipelines.underline_pipeline.draw(
+            &self.devices.device_context,
+            &self.resources.viewport,
+            &self.globals.global_params_buffer,
+            D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
+            4,
+            underlines.len() as u32,
+        )
+    }
+
+    fn draw_monochrome_sprites(
+        &mut self,
+        texture_id: AtlasTextureId,
+        sprites: &[MonochromeSprite],
+    ) -> Result<()> {
+        if sprites.is_empty() {
+            return Ok(());
+        }
+        self.pipelines.mono_sprites.update_buffer(
+            &self.devices.device,
+            &self.devices.device_context,
+            sprites,
+        )?;
+        let texture_view = self.atlas.get_texture_view(texture_id);
+        self.pipelines.mono_sprites.draw_with_texture(
+            &self.devices.device_context,
+            &texture_view,
+            &self.resources.viewport,
+            &self.globals.global_params_buffer,
+            &self.globals.sampler,
+            sprites.len() as u32,
+        )
+    }
+
+    fn draw_polychrome_sprites(
+        &mut self,
+        texture_id: AtlasTextureId,
+        sprites: &[PolychromeSprite],
+    ) -> Result<()> {
+        if sprites.is_empty() {
+            return Ok(());
+        }
+        self.pipelines.poly_sprites.update_buffer(
+            &self.devices.device,
+            &self.devices.device_context,
+            sprites,
+        )?;
+        let texture_view = self.atlas.get_texture_view(texture_id);
+        self.pipelines.poly_sprites.draw_with_texture(
+            &self.devices.device_context,
+            &texture_view,
+            &self.resources.viewport,
+            &self.globals.global_params_buffer,
+            &self.globals.sampler,
+            sprites.len() as u32,
+        )
+    }
+
+    fn draw_surfaces(&mut self, surfaces: &[PaintSurface]) -> Result<()> {
+        if surfaces.is_empty() {
+            return Ok(());
+        }
+        Ok(())
+    }
+
+    pub(crate) fn gpu_specs(&self) -> Result<GpuSpecs> {
+        let desc = unsafe { self.devices.adapter.GetDesc1() }?;
+        let is_software_emulated = (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE.0 as u32) != 0;
+        let device_name = String::from_utf16_lossy(&desc.Description)
+            .trim_matches(char::from(0))
+            .to_string();
+        let driver_name = match desc.VendorId {
+            0x10DE => "NVIDIA Corporation".to_string(),
+            0x1002 => "AMD Corporation".to_string(),
+            0x8086 => "Intel Corporation".to_string(),
+            id => format!("Unknown Vendor (ID: {:#X})", id),
+        };
+        let driver_version = match desc.VendorId {
+            0x10DE => nvidia::get_driver_version(),
+            0x1002 => amd::get_driver_version(),
+            // For Intel and other vendors, we use the DXGI API to get the driver version.
+            _ => dxgi::get_driver_version(&self.devices.adapter),
+        }
+        .context("Failed to get gpu driver info")
+        .log_err()
+        .unwrap_or("Unknown Driver".to_string());
+        Ok(GpuSpecs {
+            is_software_emulated,
+            device_name,
+            driver_name,
+            driver_info: driver_version,
+        })
+    }
+}
+
+impl DirectXResources {
+    pub fn new(
+        devices: &DirectXDevices,
+        width: u32,
+        height: u32,
+        hwnd: HWND,
+        disable_direct_composition: bool,
+    ) -> Result<ManuallyDrop<Self>> {
+        let swap_chain = if disable_direct_composition {
+            create_swap_chain(&devices.dxgi_factory, &devices.device, hwnd, width, height)?
+        } else {
+            create_swap_chain_for_composition(
+                &devices.dxgi_factory,
+                &devices.device,
+                width,
+                height,
+            )?
+        };
+
+        let (
+            render_target,
+            render_target_view,
+            path_intermediate_texture,
+            path_intermediate_srv,
+            path_intermediate_msaa_texture,
+            path_intermediate_msaa_view,
+            viewport,
+        ) = create_resources(devices, &swap_chain, width, height)?;
+        set_rasterizer_state(&devices.device, &devices.device_context)?;
+
+        Ok(ManuallyDrop::new(Self {
+            swap_chain,
+            render_target,
+            render_target_view,
+            path_intermediate_texture,
+            path_intermediate_msaa_texture,
+            path_intermediate_msaa_view,
+            path_intermediate_srv,
+            viewport,
+            width,
+            height,
+        }))
+    }
+
+    #[inline]
+    fn recreate_resources(
+        &mut self,
+        devices: &DirectXDevices,
+        width: u32,
+        height: u32,
+    ) -> Result<()> {
+        let (
+            render_target,
+            render_target_view,
+            path_intermediate_texture,
+            path_intermediate_srv,
+            path_intermediate_msaa_texture,
+            path_intermediate_msaa_view,
+            viewport,
+        ) = create_resources(devices, &self.swap_chain, width, height)?;
+        self.render_target = render_target;
+        self.render_target_view = render_target_view;
+        self.path_intermediate_texture = path_intermediate_texture;
+        self.path_intermediate_msaa_texture = path_intermediate_msaa_texture;
+        self.path_intermediate_msaa_view = path_intermediate_msaa_view;
+        self.path_intermediate_srv = path_intermediate_srv;
+        self.viewport = viewport;
+        self.width = width;
+        self.height = height;
+        Ok(())
+    }
+}
+
+impl DirectXRenderPipelines {
+    pub fn new(device: &ID3D11Device) -> Result<Self> {
+        let shadow_pipeline = PipelineState::new(
+            device,
+            "shadow_pipeline",
+            ShaderModule::Shadow,
+            4,
+            create_blend_state(device)?,
+        )?;
+        let quad_pipeline = PipelineState::new(
+            device,
+            "quad_pipeline",
+            ShaderModule::Quad,
+            64,
+            create_blend_state(device)?,
+        )?;
+        let path_rasterization_pipeline = PipelineState::new(
+            device,
+            "path_rasterization_pipeline",
+            ShaderModule::PathRasterization,
+            32,
+            create_blend_state_for_path_rasterization(device)?,
+        )?;
+        let path_sprite_pipeline = PipelineState::new(
+            device,
+            "path_sprite_pipeline",
+            ShaderModule::PathSprite,
+            4,
+            create_blend_state_for_path_sprite(device)?,
+        )?;
+        let underline_pipeline = PipelineState::new(
+            device,
+            "underline_pipeline",
+            ShaderModule::Underline,
+            4,
+            create_blend_state(device)?,
+        )?;
+        let mono_sprites = PipelineState::new(
+            device,
+            "monochrome_sprite_pipeline",
+            ShaderModule::MonochromeSprite,
+            512,
+            create_blend_state(device)?,
+        )?;
+        let poly_sprites = PipelineState::new(
+            device,
+            "polychrome_sprite_pipeline",
+            ShaderModule::PolychromeSprite,
+            16,
+            create_blend_state(device)?,
+        )?;
+
+        Ok(Self {
+            shadow_pipeline,
+            quad_pipeline,
+            path_rasterization_pipeline,
+            path_sprite_pipeline,
+            underline_pipeline,
+            mono_sprites,
+            poly_sprites,
+        })
+    }
+}
+
+impl DirectComposition {
+    pub fn new(dxgi_device: &IDXGIDevice, hwnd: HWND) -> Result<Self> {
+        let comp_device = get_comp_device(&dxgi_device)?;
+        let comp_target = unsafe { comp_device.CreateTargetForHwnd(hwnd, true) }?;
+        let comp_visual = unsafe { comp_device.CreateVisual() }?;
+
+        Ok(Self {
+            comp_device,
+            comp_target,
+            comp_visual,
+        })
+    }
+
+    pub fn set_swap_chain(&self, swap_chain: &IDXGISwapChain1) -> Result<()> {
+        unsafe {
+            self.comp_visual.SetContent(swap_chain)?;
+            self.comp_target.SetRoot(&self.comp_visual)?;
+            self.comp_device.Commit()?;
+        }
+        Ok(())
+    }
+}
+
+impl DirectXGlobalElements {
+    pub fn new(device: &ID3D11Device) -> Result<Self> {
+        let global_params_buffer = unsafe {
+            let desc = D3D11_BUFFER_DESC {
+                ByteWidth: std::mem::size_of::<GlobalParams>() as u32,
+                Usage: D3D11_USAGE_DYNAMIC,
+                BindFlags: D3D11_BIND_CONSTANT_BUFFER.0 as u32,
+                CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
+                ..Default::default()
+            };
+            let mut buffer = None;
+            device.CreateBuffer(&desc, None, Some(&mut buffer))?;
+            [buffer]
+        };
+
+        let sampler = unsafe {
+            let desc = D3D11_SAMPLER_DESC {
+                Filter: D3D11_FILTER_MIN_MAG_MIP_LINEAR,
+                AddressU: D3D11_TEXTURE_ADDRESS_WRAP,
+                AddressV: D3D11_TEXTURE_ADDRESS_WRAP,
+                AddressW: D3D11_TEXTURE_ADDRESS_WRAP,
+                MipLODBias: 0.0,
+                MaxAnisotropy: 1,
+                ComparisonFunc: D3D11_COMPARISON_ALWAYS,
+                BorderColor: [0.0; 4],
+                MinLOD: 0.0,
+                MaxLOD: D3D11_FLOAT32_MAX,
+            };
+            let mut output = None;
+            device.CreateSamplerState(&desc, Some(&mut output))?;
+            [output]
+        };
+
+        Ok(Self {
+            global_params_buffer,
+            sampler,
+        })
+    }
+}
+
+#[derive(Debug, Default)]
+#[repr(C)]
+struct GlobalParams {
+    viewport_size: [f32; 2],
+    _pad: u64,
+}
+
+struct PipelineState<T> {
+    label: &'static str,
+    vertex: ID3D11VertexShader,
+    fragment: ID3D11PixelShader,
+    buffer: ID3D11Buffer,
+    buffer_size: usize,
+    view: [Option<ID3D11ShaderResourceView>; 1],
+    blend_state: ID3D11BlendState,
+    _marker: std::marker::PhantomData<T>,
+}
+
+impl<T> PipelineState<T> {
+    fn new(
+        device: &ID3D11Device,
+        label: &'static str,
+        shader_module: ShaderModule,
+        buffer_size: usize,
+        blend_state: ID3D11BlendState,
+    ) -> Result<Self> {
+        let vertex = {
+            let raw_shader = RawShaderBytes::new(shader_module, ShaderTarget::Vertex)?;
+            create_vertex_shader(device, raw_shader.as_bytes())?
+        };
+        let fragment = {
+            let raw_shader = RawShaderBytes::new(shader_module, ShaderTarget::Fragment)?;
+            create_fragment_shader(device, raw_shader.as_bytes())?
+        };
+        let buffer = create_buffer(device, std::mem::size_of::<T>(), buffer_size)?;
+        let view = create_buffer_view(device, &buffer)?;
+
+        Ok(PipelineState {
+            label,
+            vertex,
+            fragment,
+            buffer,
+            buffer_size,
+            view,
+            blend_state,
+            _marker: std::marker::PhantomData,
+        })
+    }
+
+    fn update_buffer(
+        &mut self,
+        device: &ID3D11Device,
+        device_context: &ID3D11DeviceContext,
+        data: &[T],
+    ) -> Result<()> {
+        if self.buffer_size < data.len() {
+            let new_buffer_size = data.len().next_power_of_two();
+            log::info!(
+                "Updating {} buffer size from {} to {}",
+                self.label,
+                self.buffer_size,
+                new_buffer_size
+            );
+            let buffer = create_buffer(device, std::mem::size_of::<T>(), new_buffer_size)?;
+            let view = create_buffer_view(device, &buffer)?;
+            self.buffer = buffer;
+            self.view = view;
+            self.buffer_size = new_buffer_size;
+        }
+        update_buffer(device_context, &self.buffer, data)
+    }
+
+    fn draw(
+        &self,
+        device_context: &ID3D11DeviceContext,
+        viewport: &[D3D11_VIEWPORT],
+        global_params: &[Option<ID3D11Buffer>],
+        topology: D3D_PRIMITIVE_TOPOLOGY,
+        vertex_count: u32,
+        instance_count: u32,
+    ) -> Result<()> {
+        set_pipeline_state(
+            device_context,
+            &self.view,
+            topology,
+            viewport,
+            &self.vertex,
+            &self.fragment,
+            global_params,
+            &self.blend_state,
+        );
+        unsafe {
+            device_context.DrawInstanced(vertex_count, instance_count, 0, 0);
+        }
+        Ok(())
+    }
+
+    fn draw_with_texture(
+        &self,
+        device_context: &ID3D11DeviceContext,
+        texture: &[Option<ID3D11ShaderResourceView>],
+        viewport: &[D3D11_VIEWPORT],
+        global_params: &[Option<ID3D11Buffer>],
+        sampler: &[Option<ID3D11SamplerState>],
+        instance_count: u32,
+    ) -> Result<()> {
+        set_pipeline_state(
+            device_context,
+            &self.view,
+            D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
+            viewport,
+            &self.vertex,
+            &self.fragment,
+            global_params,
+            &self.blend_state,
+        );
+        unsafe {
+            device_context.PSSetSamplers(0, Some(sampler));
+            device_context.VSSetShaderResources(0, Some(texture));
+            device_context.PSSetShaderResources(0, Some(texture));
+
+            device_context.DrawInstanced(4, instance_count, 0, 0);
+        }
+        Ok(())
+    }
+}
+
+#[derive(Clone, Copy)]
+#[repr(C)]
+struct PathRasterizationSprite {
+    xy_position: Point<ScaledPixels>,
+    st_position: Point<f32>,
+    color: Background,
+    bounds: Bounds<ScaledPixels>,
+}
+
+#[derive(Clone, Copy)]
+#[repr(C)]
+struct PathSprite {
+    bounds: Bounds<ScaledPixels>,
+}
+
+impl Drop for DirectXRenderer {
+    fn drop(&mut self) {
+        #[cfg(debug_assertions)]
+        report_live_objects(&self.devices.device).ok();
+        unsafe {
+            ManuallyDrop::drop(&mut self.devices);
+            ManuallyDrop::drop(&mut self.resources);
+        }
+    }
+}
+
+impl Drop for DirectXResources {
+    fn drop(&mut self) {
+        unsafe {
+            ManuallyDrop::drop(&mut self.render_target);
+        }
+    }
+}
+
+#[inline]
+fn get_dxgi_factory() -> Result<IDXGIFactory6> {
+    #[cfg(debug_assertions)]
+    let factory_flag = if unsafe { DXGIGetDebugInterface1::<IDXGIInfoQueue>(0) }
+        .log_err()
+        .is_some()
+    {
+        DXGI_CREATE_FACTORY_DEBUG
+    } else {
+        log::warn!(
+            "Failed to get DXGI debug interface. DirectX debugging features will be disabled."
+        );
+        DXGI_CREATE_FACTORY_FLAGS::default()
+    };
+    #[cfg(not(debug_assertions))]
+    let factory_flag = DXGI_CREATE_FACTORY_FLAGS::default();
+    unsafe { Ok(CreateDXGIFactory2(factory_flag)?) }
+}
+
+fn get_adapter(dxgi_factory: &IDXGIFactory6) -> Result<IDXGIAdapter1> {
+    for adapter_index in 0.. {
+        let adapter: IDXGIAdapter1 = unsafe {
+            dxgi_factory
+                .EnumAdapterByGpuPreference(adapter_index, DXGI_GPU_PREFERENCE_MINIMUM_POWER)
+        }?;
+        if let Ok(desc) = unsafe { adapter.GetDesc1() } {
+            let gpu_name = String::from_utf16_lossy(&desc.Description)
+                .trim_matches(char::from(0))
+                .to_string();
+            log::info!("Using GPU: {}", gpu_name);
+        }
+        // Check to see whether the adapter supports Direct3D 11, but don't
+        // create the actual device yet.
+        if get_device(&adapter, None, None, None).log_err().is_some() {
+            return Ok(adapter);
+        }
+    }
+
+    unreachable!()
+}
+
+fn get_device(
+    adapter: &IDXGIAdapter1,
+    device: Option<*mut Option<ID3D11Device>>,
+    context: Option<*mut Option<ID3D11DeviceContext>>,
+    feature_level: Option<*mut D3D_FEATURE_LEVEL>,
+) -> Result<()> {
+    #[cfg(debug_assertions)]
+    let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG;
+    #[cfg(not(debug_assertions))]
+    let device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
+    unsafe {
+        D3D11CreateDevice(
+            adapter,
+            D3D_DRIVER_TYPE_UNKNOWN,
+            HMODULE::default(),
+            device_flags,
+            // 4x MSAA is required for Direct3D Feature Level 10.1 or better
+            Some(&[
+                D3D_FEATURE_LEVEL_11_1,
+                D3D_FEATURE_LEVEL_11_0,
+                D3D_FEATURE_LEVEL_10_1,
+            ]),
+            D3D11_SDK_VERSION,
+            device,
+            feature_level,
+            context,
+        )?;
+    }
+    Ok(())
+}
+
+#[inline]
+fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result<IDCompositionDevice> {
+    Ok(unsafe { DCompositionCreateDevice(dxgi_device)? })
+}
+
+fn create_swap_chain_for_composition(
+    dxgi_factory: &IDXGIFactory6,
+    device: &ID3D11Device,
+    width: u32,
+    height: u32,
+) -> Result<IDXGISwapChain1> {
+    let desc = DXGI_SWAP_CHAIN_DESC1 {
+        Width: width,
+        Height: height,
+        Format: RENDER_TARGET_FORMAT,
+        Stereo: false.into(),
+        SampleDesc: DXGI_SAMPLE_DESC {
+            Count: 1,
+            Quality: 0,
+        },
+        BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
+        BufferCount: BUFFER_COUNT as u32,
+        // Composition SwapChains only support the DXGI_SCALING_STRETCH Scaling.
+        Scaling: DXGI_SCALING_STRETCH,
+        SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
+        AlphaMode: DXGI_ALPHA_MODE_PREMULTIPLIED,
+        Flags: 0,
+    };
+    Ok(unsafe { dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? })
+}
+
+fn create_swap_chain(
+    dxgi_factory: &IDXGIFactory6,
+    device: &ID3D11Device,
+    hwnd: HWND,
+    width: u32,
+    height: u32,
+) -> Result<IDXGISwapChain1> {
+    use windows::Win32::Graphics::Dxgi::DXGI_MWA_NO_ALT_ENTER;
+
+    let desc = DXGI_SWAP_CHAIN_DESC1 {
+        Width: width,
+        Height: height,
+        Format: RENDER_TARGET_FORMAT,
+        Stereo: false.into(),
+        SampleDesc: DXGI_SAMPLE_DESC {
+            Count: 1,
+            Quality: 0,
+        },
+        BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
+        BufferCount: BUFFER_COUNT as u32,
+        Scaling: DXGI_SCALING_NONE,
+        SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
+        AlphaMode: DXGI_ALPHA_MODE_IGNORE,
+        Flags: 0,
+    };
+    let swap_chain =
+        unsafe { dxgi_factory.CreateSwapChainForHwnd(device, hwnd, &desc, None, None) }?;
+    unsafe { dxgi_factory.MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER) }?;
+    Ok(swap_chain)
+}
+
+#[inline]
+fn create_resources(
+    devices: &DirectXDevices,
+    swap_chain: &IDXGISwapChain1,
+    width: u32,
+    height: u32,
+) -> Result<(
+    ManuallyDrop<ID3D11Texture2D>,
+    [Option<ID3D11RenderTargetView>; 1],
+    ID3D11Texture2D,
+    [Option<ID3D11ShaderResourceView>; 1],
+    ID3D11Texture2D,
+    [Option<ID3D11RenderTargetView>; 1],
+    [D3D11_VIEWPORT; 1],
+)> {
+    let (render_target, render_target_view) =
+        create_render_target_and_its_view(&swap_chain, &devices.device)?;
+    let (path_intermediate_texture, path_intermediate_srv) =
+        create_path_intermediate_texture(&devices.device, width, height)?;
+    let (path_intermediate_msaa_texture, path_intermediate_msaa_view) =
+        create_path_intermediate_msaa_texture_and_view(&devices.device, width, height)?;
+    let viewport = set_viewport(&devices.device_context, width as f32, height as f32);
+    Ok((
+        render_target,
+        render_target_view,
+        path_intermediate_texture,
+        path_intermediate_srv,
+        path_intermediate_msaa_texture,
+        path_intermediate_msaa_view,
+        viewport,
+    ))
+}
+
+#[inline]
+fn create_render_target_and_its_view(
+    swap_chain: &IDXGISwapChain1,
+    device: &ID3D11Device,
+) -> Result<(
+    ManuallyDrop<ID3D11Texture2D>,
+    [Option<ID3D11RenderTargetView>; 1],
+)> {
+    let render_target: ID3D11Texture2D = unsafe { swap_chain.GetBuffer(0) }?;
+    let mut render_target_view = None;
+    unsafe { device.CreateRenderTargetView(&render_target, None, Some(&mut render_target_view))? };
+    Ok((
+        ManuallyDrop::new(render_target),
+        [Some(render_target_view.unwrap())],
+    ))
+}
+
+#[inline]
+fn create_path_intermediate_texture(
+    device: &ID3D11Device,
+    width: u32,
+    height: u32,
+) -> Result<(ID3D11Texture2D, [Option<ID3D11ShaderResourceView>; 1])> {
+    let texture = unsafe {
+        let mut output = None;
+        let desc = D3D11_TEXTURE2D_DESC {
+            Width: width,
+            Height: height,
+            MipLevels: 1,
+            ArraySize: 1,
+            Format: RENDER_TARGET_FORMAT,
+            SampleDesc: DXGI_SAMPLE_DESC {
+                Count: 1,
+                Quality: 0,
+            },
+            Usage: D3D11_USAGE_DEFAULT,
+            BindFlags: (D3D11_BIND_RENDER_TARGET.0 | D3D11_BIND_SHADER_RESOURCE.0) as u32,
+            CPUAccessFlags: 0,
+            MiscFlags: 0,
+        };
+        device.CreateTexture2D(&desc, None, Some(&mut output))?;
+        output.unwrap()
+    };
+
+    let mut shader_resource_view = None;
+    unsafe { device.CreateShaderResourceView(&texture, None, Some(&mut shader_resource_view))? };
+
+    Ok((texture, [Some(shader_resource_view.unwrap())]))
+}
+
+#[inline]
+fn create_path_intermediate_msaa_texture_and_view(
+    device: &ID3D11Device,
+    width: u32,
+    height: u32,
+) -> Result<(ID3D11Texture2D, [Option<ID3D11RenderTargetView>; 1])> {
+    let msaa_texture = unsafe {
+        let mut output = None;
+        let desc = D3D11_TEXTURE2D_DESC {
+            Width: width,
+            Height: height,
+            MipLevels: 1,
+            ArraySize: 1,
+            Format: RENDER_TARGET_FORMAT,
+            SampleDesc: DXGI_SAMPLE_DESC {
+                Count: PATH_MULTISAMPLE_COUNT,
+                Quality: D3D11_STANDARD_MULTISAMPLE_PATTERN.0 as u32,
+            },
+            Usage: D3D11_USAGE_DEFAULT,
+            BindFlags: D3D11_BIND_RENDER_TARGET.0 as u32,
+            CPUAccessFlags: 0,
+            MiscFlags: 0,
+        };
+        device.CreateTexture2D(&desc, None, Some(&mut output))?;
+        output.unwrap()
+    };
+    let mut msaa_view = None;
+    unsafe { device.CreateRenderTargetView(&msaa_texture, None, Some(&mut msaa_view))? };
+    Ok((msaa_texture, [Some(msaa_view.unwrap())]))
+}
+
+#[inline]
+fn set_viewport(
+    device_context: &ID3D11DeviceContext,
+    width: f32,
+    height: f32,
+) -> [D3D11_VIEWPORT; 1] {
+    let viewport = [D3D11_VIEWPORT {
+        TopLeftX: 0.0,
+        TopLeftY: 0.0,
+        Width: width,
+        Height: height,
+        MinDepth: 0.0,
+        MaxDepth: 1.0,
+    }];
+    unsafe { device_context.RSSetViewports(Some(&viewport)) };
+    viewport
+}
+
+#[inline]
+fn set_rasterizer_state(device: &ID3D11Device, device_context: &ID3D11DeviceContext) -> Result<()> {
+    let desc = D3D11_RASTERIZER_DESC {
+        FillMode: D3D11_FILL_SOLID,
+        CullMode: D3D11_CULL_NONE,
+        FrontCounterClockwise: false.into(),
+        DepthBias: 0,
+        DepthBiasClamp: 0.0,
+        SlopeScaledDepthBias: 0.0,
+        DepthClipEnable: true.into(),
+        ScissorEnable: false.into(),
+        MultisampleEnable: true.into(),
+        AntialiasedLineEnable: false.into(),
+    };
+    let rasterizer_state = unsafe {
+        let mut state = None;
+        device.CreateRasterizerState(&desc, Some(&mut state))?;
+        state.unwrap()
+    };
+    unsafe { device_context.RSSetState(&rasterizer_state) };
+    Ok(())
+}
+
+// https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_blend_desc
+#[inline]
+fn create_blend_state(device: &ID3D11Device) -> Result<ID3D11BlendState> {
+    // If the feature level is set to greater than D3D_FEATURE_LEVEL_9_3, the display
+    // device performs the blend in linear space, which is ideal.
+    let mut desc = D3D11_BLEND_DESC::default();
+    desc.RenderTarget[0].BlendEnable = true.into();
+    desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
+    desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
+    desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
+    desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
+    desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
+    desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
+    desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8;
+    unsafe {
+        let mut state = None;
+        device.CreateBlendState(&desc, Some(&mut state))?;
+        Ok(state.unwrap())
+    }
+}
+
+#[inline]
+fn create_blend_state_for_path_rasterization(device: &ID3D11Device) -> Result<ID3D11BlendState> {
+    // If the feature level is set to greater than D3D_FEATURE_LEVEL_9_3, the display
+    // device performs the blend in linear space, which is ideal.
+    let mut desc = D3D11_BLEND_DESC::default();
+    desc.RenderTarget[0].BlendEnable = true.into();
+    desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
+    desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
+    desc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
+    desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
+    desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
+    desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
+    desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8;
+    unsafe {
+        let mut state = None;
+        device.CreateBlendState(&desc, Some(&mut state))?;
+        Ok(state.unwrap())
+    }
+}
+
+#[inline]
+fn create_blend_state_for_path_sprite(device: &ID3D11Device) -> Result<ID3D11BlendState> {
+    // If the feature level is set to greater than D3D_FEATURE_LEVEL_9_3, the display
+    // device performs the blend in linear space, which is ideal.
+    let mut desc = D3D11_BLEND_DESC::default();
+    desc.RenderTarget[0].BlendEnable = true.into();
+    desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
+    desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
+    desc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
+    desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
+    desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
+    desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
+    desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8;
+    unsafe {
+        let mut state = None;
+        device.CreateBlendState(&desc, Some(&mut state))?;
+        Ok(state.unwrap())
+    }
+}
+
+#[inline]
+fn create_vertex_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11VertexShader> {
+    unsafe {
+        let mut shader = None;
+        device.CreateVertexShader(bytes, None, Some(&mut shader))?;
+        Ok(shader.unwrap())
+    }
+}
+
+#[inline]
+fn create_fragment_shader(device: &ID3D11Device, bytes: &[u8]) -> Result<ID3D11PixelShader> {
+    unsafe {
+        let mut shader = None;
+        device.CreatePixelShader(bytes, None, Some(&mut shader))?;
+        Ok(shader.unwrap())
+    }
+}
+
+#[inline]
+fn create_buffer(
+    device: &ID3D11Device,
+    element_size: usize,
+    buffer_size: usize,
+) -> Result<ID3D11Buffer> {
+    let desc = D3D11_BUFFER_DESC {
+        ByteWidth: (element_size * buffer_size) as u32,
+        Usage: D3D11_USAGE_DYNAMIC,
+        BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
+        CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
+        MiscFlags: D3D11_RESOURCE_MISC_BUFFER_STRUCTURED.0 as u32,
+        StructureByteStride: element_size as u32,
+    };
+    let mut buffer = None;
+    unsafe { device.CreateBuffer(&desc, None, Some(&mut buffer)) }?;
+    Ok(buffer.unwrap())
+}
+
+#[inline]
+fn create_buffer_view(
+    device: &ID3D11Device,
+    buffer: &ID3D11Buffer,
+) -> Result<[Option<ID3D11ShaderResourceView>; 1]> {
+    let mut view = None;
+    unsafe { device.CreateShaderResourceView(buffer, None, Some(&mut view)) }?;
+    Ok([view])
+}
+
+#[inline]
+fn update_buffer<T>(
+    device_context: &ID3D11DeviceContext,
+    buffer: &ID3D11Buffer,
+    data: &[T],
+) -> Result<()> {
+    unsafe {
+        let mut dest = std::mem::zeroed();
+        device_context.Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, Some(&mut dest))?;
+        std::ptr::copy_nonoverlapping(data.as_ptr(), dest.pData as _, data.len());
+        device_context.Unmap(buffer, 0);
+    }
+    Ok(())
+}
+
+#[inline]
+fn set_pipeline_state(
+    device_context: &ID3D11DeviceContext,
+    buffer_view: &[Option<ID3D11ShaderResourceView>],
+    topology: D3D_PRIMITIVE_TOPOLOGY,
+    viewport: &[D3D11_VIEWPORT],
+    vertex_shader: &ID3D11VertexShader,
+    fragment_shader: &ID3D11PixelShader,
+    global_params: &[Option<ID3D11Buffer>],
+    blend_state: &ID3D11BlendState,
+) {
+    unsafe {
+        device_context.VSSetShaderResources(1, Some(buffer_view));
+        device_context.PSSetShaderResources(1, Some(buffer_view));
+        device_context.IASetPrimitiveTopology(topology);
+        device_context.RSSetViewports(Some(viewport));
+        device_context.VSSetShader(vertex_shader, None);
+        device_context.PSSetShader(fragment_shader, None);
+        device_context.VSSetConstantBuffers(0, Some(global_params));
+        device_context.PSSetConstantBuffers(0, Some(global_params));
+        device_context.OMSetBlendState(blend_state, None, 0xFFFFFFFF);
+    }
+}
+
+#[cfg(debug_assertions)]
+fn report_live_objects(device: &ID3D11Device) -> Result<()> {
+    let debug_device: ID3D11Debug = device.cast()?;
+    unsafe {
+        debug_device.ReportLiveDeviceObjects(D3D11_RLDO_DETAIL)?;
+    }
+    Ok(())
+}
+
+const BUFFER_COUNT: usize = 3;
+
+mod shader_resources {
+    use anyhow::Result;
+
+    #[cfg(debug_assertions)]
+    use windows::{
+        Win32::Graphics::Direct3D::{
+            Fxc::{D3DCOMPILE_DEBUG, D3DCOMPILE_SKIP_OPTIMIZATION, D3DCompileFromFile},
+            ID3DBlob,
+        },
+        core::{HSTRING, PCSTR},
+    };
+
+    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
+    pub(super) enum ShaderModule {
+        Quad,
+        Shadow,
+        Underline,
+        PathRasterization,
+        PathSprite,
+        MonochromeSprite,
+        PolychromeSprite,
+    }
+
+    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
+    pub(super) enum ShaderTarget {
+        Vertex,
+        Fragment,
+    }
+
+    pub(super) struct RawShaderBytes<'t> {
+        inner: &'t [u8],
+
+        #[cfg(debug_assertions)]
+        _blob: ID3DBlob,
+    }
+
+    impl<'t> RawShaderBytes<'t> {
+        pub(super) fn new(module: ShaderModule, target: ShaderTarget) -> Result<Self> {
+            #[cfg(not(debug_assertions))]
+            {
+                Ok(Self::from_bytes(module, target))
+            }
+            #[cfg(debug_assertions)]
+            {
+                let blob = build_shader_blob(module, target)?;
+                let inner = unsafe {
+                    std::slice::from_raw_parts(
+                        blob.GetBufferPointer() as *const u8,
+                        blob.GetBufferSize(),
+                    )
+                };
+                Ok(Self { inner, _blob: blob })
+            }
+        }
+
+        pub(super) fn as_bytes(&'t self) -> &'t [u8] {
+            self.inner
+        }
+
+        #[cfg(not(debug_assertions))]
+        fn from_bytes(module: ShaderModule, target: ShaderTarget) -> Self {
+            let bytes = match module {
+                ShaderModule::Quad => match target {
+                    ShaderTarget::Vertex => QUAD_VERTEX_BYTES,
+                    ShaderTarget::Fragment => QUAD_FRAGMENT_BYTES,
+                },
+                ShaderModule::Shadow => match target {
+                    ShaderTarget::Vertex => SHADOW_VERTEX_BYTES,
+                    ShaderTarget::Fragment => SHADOW_FRAGMENT_BYTES,
+                },
+                ShaderModule::Underline => match target {
+                    ShaderTarget::Vertex => UNDERLINE_VERTEX_BYTES,
+                    ShaderTarget::Fragment => UNDERLINE_FRAGMENT_BYTES,
+                },
+                ShaderModule::PathRasterization => match target {
+                    ShaderTarget::Vertex => PATH_RASTERIZATION_VERTEX_BYTES,
+                    ShaderTarget::Fragment => PATH_RASTERIZATION_FRAGMENT_BYTES,
+                },
+                ShaderModule::PathSprite => match target {
+                    ShaderTarget::Vertex => PATH_SPRITE_VERTEX_BYTES,
+                    ShaderTarget::Fragment => PATH_SPRITE_FRAGMENT_BYTES,
+                },
+                ShaderModule::MonochromeSprite => match target {
+                    ShaderTarget::Vertex => MONOCHROME_SPRITE_VERTEX_BYTES,
+                    ShaderTarget::Fragment => MONOCHROME_SPRITE_FRAGMENT_BYTES,
+                },
+                ShaderModule::PolychromeSprite => match target {
+                    ShaderTarget::Vertex => POLYCHROME_SPRITE_VERTEX_BYTES,
+                    ShaderTarget::Fragment => POLYCHROME_SPRITE_FRAGMENT_BYTES,
+                },
+            };
+            Self { inner: bytes }
+        }
+    }
+
+    #[cfg(debug_assertions)]
+    pub(super) fn build_shader_blob(entry: ShaderModule, target: ShaderTarget) -> Result<ID3DBlob> {
+        unsafe {
+            let entry = format!(
+                "{}_{}\0",
+                entry.as_str(),
+                match target {
+                    ShaderTarget::Vertex => "vertex",
+                    ShaderTarget::Fragment => "fragment",
+                }
+            );
+            let target = match target {
+                ShaderTarget::Vertex => "vs_4_1\0",
+                ShaderTarget::Fragment => "ps_4_1\0",
+            };
+
+            let mut compile_blob = None;
+            let mut error_blob = None;
+            let shader_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
+                .join("src/platform/windows/shaders.hlsl")
+                .canonicalize()?;
+
+            let entry_point = PCSTR::from_raw(entry.as_ptr());
+            let target_cstr = PCSTR::from_raw(target.as_ptr());
+
+            let ret = D3DCompileFromFile(
+                &HSTRING::from(shader_path.to_str().unwrap()),
+                None,
+                None,
+                entry_point,
+                target_cstr,
+                D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION,
+                0,
+                &mut compile_blob,
+                Some(&mut error_blob),
+            );
+            if ret.is_err() {
+                let Some(error_blob) = error_blob else {
+                    return Err(anyhow::anyhow!("{ret:?}"));
+                };
+
+                let error_string =
+                    std::ffi::CStr::from_ptr(error_blob.GetBufferPointer() as *const i8)
+                        .to_string_lossy();
+                log::error!("Shader compile error: {}", error_string);
+                return Err(anyhow::anyhow!("Compile error: {}", error_string));
+            }
+            Ok(compile_blob.unwrap())
+        }
+    }
+
+    #[cfg(not(debug_assertions))]
+    include!(concat!(env!("OUT_DIR"), "/shaders_bytes.rs"));
+
+    #[cfg(debug_assertions)]
+    impl ShaderModule {
+        pub fn as_str(&self) -> &str {
+            match self {
+                ShaderModule::Quad => "quad",
+                ShaderModule::Shadow => "shadow",
+                ShaderModule::Underline => "underline",
+                ShaderModule::PathRasterization => "path_rasterization",
+                ShaderModule::PathSprite => "path_sprite",
+                ShaderModule::MonochromeSprite => "monochrome_sprite",
+                ShaderModule::PolychromeSprite => "polychrome_sprite",
+            }
+        }
+    }
+}
+
+mod nvidia {
+    use std::{
+        ffi::CStr,
+        os::raw::{c_char, c_int, c_uint},
+    };
+
+    use anyhow::{Context, Result};
+    use windows::{
+        Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryA},
+        core::s,
+    };
+
+    // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_lite_common.h#L180
+    const NVAPI_SHORT_STRING_MAX: usize = 64;
+
+    // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_lite_common.h#L235
+    #[allow(non_camel_case_types)]
+    type NvAPI_ShortString = [c_char; NVAPI_SHORT_STRING_MAX];
+
+    // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_lite_common.h#L447
+    #[allow(non_camel_case_types)]
+    type NvAPI_SYS_GetDriverAndBranchVersion_t = unsafe extern "C" fn(
+        driver_version: *mut c_uint,
+        build_branch_string: *mut NvAPI_ShortString,
+    ) -> c_int;
+
+    pub(super) fn get_driver_version() -> Result<String> {
+        unsafe {
+            // Try to load the NVIDIA driver DLL
+            #[cfg(target_pointer_width = "64")]
+            let nvidia_dll = LoadLibraryA(s!("nvapi64.dll")).context("Can't load nvapi64.dll")?;
+            #[cfg(target_pointer_width = "32")]
+            let nvidia_dll = LoadLibraryA(s!("nvapi.dll")).context("Can't load nvapi.dll")?;
+
+            let nvapi_query_addr = GetProcAddress(nvidia_dll, s!("nvapi_QueryInterface"))
+                .ok_or_else(|| anyhow::anyhow!("Failed to get nvapi_QueryInterface address"))?;
+            let nvapi_query: extern "C" fn(u32) -> *mut () = std::mem::transmute(nvapi_query_addr);
+
+            // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_interface.h#L41
+            let nvapi_get_driver_version_ptr = nvapi_query(0x2926aaad);
+            if nvapi_get_driver_version_ptr.is_null() {
+                anyhow::bail!("Failed to get NVIDIA driver version function pointer");
+            }
+            let nvapi_get_driver_version: NvAPI_SYS_GetDriverAndBranchVersion_t =
+                std::mem::transmute(nvapi_get_driver_version_ptr);
+
+            let mut driver_version: c_uint = 0;
+            let mut build_branch_string: NvAPI_ShortString = [0; NVAPI_SHORT_STRING_MAX];
+            let result = nvapi_get_driver_version(
+                &mut driver_version as *mut c_uint,
+                &mut build_branch_string as *mut NvAPI_ShortString,
+            );
+
+            if result != 0 {
+                anyhow::bail!(
+                    "Failed to get NVIDIA driver version, error code: {}",
+                    result
+                );
+            }
+            let major = driver_version / 100;
+            let minor = driver_version % 100;
+            let branch_string = CStr::from_ptr(build_branch_string.as_ptr());
+            Ok(format!(
+                "{}.{} {}",
+                major,
+                minor,
+                branch_string.to_string_lossy()
+            ))
+        }
+    }
+}
+
+mod amd {
+    use std::os::raw::{c_char, c_int, c_void};
+
+    use anyhow::{Context, Result};
+    use windows::{
+        Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryA},
+        core::s,
+    };
+
+    // https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/blob/5d8812d703d0335741b6f7ffc37838eeb8b967f7/ags_lib/inc/amd_ags.h#L145
+    const AGS_CURRENT_VERSION: i32 = (6 << 22) | (3 << 12);
+
+    // https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/blob/5d8812d703d0335741b6f7ffc37838eeb8b967f7/ags_lib/inc/amd_ags.h#L204
+    // This is an opaque type, using struct to represent it properly for FFI
+    #[repr(C)]
+    struct AGSContext {
+        _private: [u8; 0],
+    }
+
+    #[repr(C)]
+    pub struct AGSGPUInfo {
+        pub driver_version: *const c_char,
+        pub radeon_software_version: *const c_char,
+        pub num_devices: c_int,
+        pub devices: *mut c_void,
+    }
+
+    // https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/blob/5d8812d703d0335741b6f7ffc37838eeb8b967f7/ags_lib/inc/amd_ags.h#L429
+    #[allow(non_camel_case_types)]
+    type agsInitialize_t = unsafe extern "C" fn(
+        version: c_int,
+        config: *const c_void,
+        context: *mut *mut AGSContext,
+        gpu_info: *mut AGSGPUInfo,
+    ) -> c_int;
+
+    // https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/blob/5d8812d703d0335741b6f7ffc37838eeb8b967f7/ags_lib/inc/amd_ags.h#L436
+    #[allow(non_camel_case_types)]
+    type agsDeInitialize_t = unsafe extern "C" fn(context: *mut AGSContext) -> c_int;
+
+    pub(super) fn get_driver_version() -> Result<String> {
+        unsafe {
+            #[cfg(target_pointer_width = "64")]
+            let amd_dll =
+                LoadLibraryA(s!("amd_ags_x64.dll")).context("Failed to load AMD AGS library")?;
+            #[cfg(target_pointer_width = "32")]
+            let amd_dll =
+                LoadLibraryA(s!("amd_ags_x86.dll")).context("Failed to load AMD AGS library")?;
+
+            let ags_initialize_addr = GetProcAddress(amd_dll, s!("agsInitialize"))
+                .ok_or_else(|| anyhow::anyhow!("Failed to get agsInitialize address"))?;
+            let ags_deinitialize_addr = GetProcAddress(amd_dll, s!("agsDeInitialize"))
+                .ok_or_else(|| anyhow::anyhow!("Failed to get agsDeInitialize address"))?;
+
+            let ags_initialize: agsInitialize_t = std::mem::transmute(ags_initialize_addr);
+            let ags_deinitialize: agsDeInitialize_t = std::mem::transmute(ags_deinitialize_addr);
+
+            let mut context: *mut AGSContext = std::ptr::null_mut();
+            let mut gpu_info: AGSGPUInfo = AGSGPUInfo {
+                driver_version: std::ptr::null(),
+                radeon_software_version: std::ptr::null(),
+                num_devices: 0,
+                devices: std::ptr::null_mut(),
+            };
+
+            let result = ags_initialize(
+                AGS_CURRENT_VERSION,
+                std::ptr::null(),
+                &mut context,
+                &mut gpu_info,
+            );
+            if result != 0 {
+                anyhow::bail!("Failed to initialize AMD AGS, error code: {}", result);
+            }
+
+            // Vulkan acctually returns this as the driver version
+            let software_version = if !gpu_info.radeon_software_version.is_null() {
+                std::ffi::CStr::from_ptr(gpu_info.radeon_software_version)
+                    .to_string_lossy()
+                    .into_owned()
+            } else {
+                "Unknown Radeon Software Version".to_string()
+            };
+
+            let driver_version = if !gpu_info.driver_version.is_null() {
+                std::ffi::CStr::from_ptr(gpu_info.driver_version)
+                    .to_string_lossy()
+                    .into_owned()
+            } else {
+                "Unknown Radeon Driver Version".to_string()
+            };
+
+            ags_deinitialize(context);
+            Ok(format!("{} ({})", software_version, driver_version))
+        }
+    }
+}
+
+mod dxgi {
+    use windows::{
+        Win32::Graphics::Dxgi::{IDXGIAdapter1, IDXGIDevice},
+        core::Interface,
+    };
+
+    pub(super) fn get_driver_version(adapter: &IDXGIAdapter1) -> anyhow::Result<String> {
+        let number = unsafe { adapter.CheckInterfaceSupport(&IDXGIDevice::IID as _) }?;
+        Ok(format!(
+            "{}.{}.{}.{}",
+            number >> 48,
+            (number >> 32) & 0xFFFF,
+            (number >> 16) & 0xFFFF,
+            number & 0xFFFF
+        ))
+    }
+}

crates/gpui/src/platform/windows/events.rs πŸ”—

@@ -23,6 +23,7 @@ pub(crate) const WM_GPUI_CURSOR_STYLE_CHANGED: u32 = WM_USER + 1;
 pub(crate) const WM_GPUI_CLOSE_ONE_WINDOW: u32 = WM_USER + 2;
 pub(crate) const WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD: u32 = WM_USER + 3;
 pub(crate) const WM_GPUI_DOCK_MENU_ACTION: u32 = WM_USER + 4;
+pub(crate) const WM_GPUI_FORCE_UPDATE_WINDOW: u32 = WM_USER + 5;
 
 const SIZE_MOVE_LOOP_TIMER_ID: usize = 1;
 const AUTO_HIDE_TASKBAR_THICKNESS_PX: i32 = 1;
@@ -37,6 +38,7 @@ pub(crate) fn handle_msg(
     let handled = match msg {
         WM_ACTIVATE => handle_activate_msg(wparam, state_ptr),
         WM_CREATE => handle_create_msg(handle, state_ptr),
+        WM_DEVICECHANGE => handle_device_change_msg(handle, wparam, state_ptr),
         WM_MOVE => handle_move_msg(handle, lparam, state_ptr),
         WM_SIZE => handle_size_msg(wparam, lparam, state_ptr),
         WM_GETMINMAXINFO => handle_get_min_max_info_msg(lparam, state_ptr),
@@ -48,7 +50,7 @@ pub(crate) fn handle_msg(
         WM_DISPLAYCHANGE => handle_display_change_msg(handle, state_ptr),
         WM_NCHITTEST => handle_hit_test_msg(handle, msg, wparam, lparam, state_ptr),
         WM_PAINT => handle_paint_msg(handle, state_ptr),
-        WM_CLOSE => handle_close_msg(handle, state_ptr),
+        WM_CLOSE => handle_close_msg(state_ptr),
         WM_DESTROY => handle_destroy_msg(handle, state_ptr),
         WM_MOUSEMOVE => handle_mouse_move_msg(handle, lparam, wparam, state_ptr),
         WM_MOUSELEAVE | WM_NCMOUSELEAVE => handle_mouse_leave_msg(state_ptr),
@@ -96,6 +98,7 @@ pub(crate) fn handle_msg(
         WM_SETTINGCHANGE => handle_system_settings_changed(handle, wparam, lparam, state_ptr),
         WM_INPUTLANGCHANGE => handle_input_language_changed(lparam, state_ptr),
         WM_GPUI_CURSOR_STYLE_CHANGED => handle_cursor_changed(lparam, state_ptr),
+        WM_GPUI_FORCE_UPDATE_WINDOW => draw_window(handle, true, state_ptr),
         _ => None,
     };
     if let Some(n) = handled {
@@ -181,11 +184,9 @@ fn handle_size_msg(
     let new_size = size(DevicePixels(width), DevicePixels(height));
     let scale_factor = lock.scale_factor;
     if lock.restore_from_minimized.is_some() {
-        lock.renderer
-            .update_drawable_size_even_if_unchanged(new_size);
         lock.callbacks.request_frame = lock.restore_from_minimized.take();
     } else {
-        lock.renderer.update_drawable_size(new_size);
+        lock.renderer.resize(new_size).log_err();
     }
     let new_size = new_size.to_pixels(scale_factor);
     lock.logical_size = new_size;
@@ -238,40 +239,14 @@ fn handle_timer_msg(
 }
 
 fn handle_paint_msg(handle: HWND, state_ptr: Rc<WindowsWindowStatePtr>) -> Option<isize> {
-    let mut lock = state_ptr.state.borrow_mut();
-    if let Some(mut request_frame) = lock.callbacks.request_frame.take() {
-        drop(lock);
-        request_frame(Default::default());
-        state_ptr.state.borrow_mut().callbacks.request_frame = Some(request_frame);
-    }
-    unsafe { ValidateRect(Some(handle), None).ok().log_err() };
-    Some(0)
+    draw_window(handle, false, state_ptr)
 }
 
-fn handle_close_msg(handle: HWND, state_ptr: Rc<WindowsWindowStatePtr>) -> Option<isize> {
-    let mut lock = state_ptr.state.borrow_mut();
-    let output = if let Some(mut callback) = lock.callbacks.should_close.take() {
-        drop(lock);
-        let should_close = callback();
-        state_ptr.state.borrow_mut().callbacks.should_close = Some(callback);
-        if should_close { None } else { Some(0) }
-    } else {
-        None
-    };
-
-    // Workaround as window close animation is not played with `WS_EX_LAYERED` enabled.
-    if output.is_none() {
-        unsafe {
-            let current_style = get_window_long(handle, GWL_EXSTYLE);
-            set_window_long(
-                handle,
-                GWL_EXSTYLE,
-                current_style & !WS_EX_LAYERED.0 as isize,
-            );
-        }
-    }
-
-    output
+fn handle_close_msg(state_ptr: Rc<WindowsWindowStatePtr>) -> Option<isize> {
+    let mut callback = state_ptr.state.borrow_mut().callbacks.should_close.take()?;
+    let should_close = callback();
+    state_ptr.state.borrow_mut().callbacks.should_close = Some(callback);
+    if should_close { None } else { Some(0) }
 }
 
 fn handle_destroy_msg(handle: HWND, state_ptr: Rc<WindowsWindowStatePtr>) -> Option<isize> {
@@ -1223,6 +1198,53 @@ fn handle_input_language_changed(
     Some(0)
 }
 
+fn handle_device_change_msg(
+    handle: HWND,
+    wparam: WPARAM,
+    state_ptr: Rc<WindowsWindowStatePtr>,
+) -> Option<isize> {
+    if wparam.0 == DBT_DEVNODES_CHANGED as usize {
+        // The reason for sending this message is to actually trigger a redraw of the window.
+        unsafe {
+            PostMessageW(
+                Some(handle),
+                WM_GPUI_FORCE_UPDATE_WINDOW,
+                WPARAM(0),
+                LPARAM(0),
+            )
+            .log_err();
+        }
+        // If the GPU device is lost, this redraw will take care of recreating the device context.
+        // The WM_GPUI_FORCE_UPDATE_WINDOW message will take care of redrawing the window, after
+        // the device context has been recreated.
+        draw_window(handle, true, state_ptr)
+    } else {
+        // Other device change messages are not handled.
+        None
+    }
+}
+
+#[inline]
+fn draw_window(
+    handle: HWND,
+    force_render: bool,
+    state_ptr: Rc<WindowsWindowStatePtr>,
+) -> Option<isize> {
+    let mut request_frame = state_ptr
+        .state
+        .borrow_mut()
+        .callbacks
+        .request_frame
+        .take()?;
+    request_frame(RequestFrameOptions {
+        require_presentation: false,
+        force_render,
+    });
+    state_ptr.state.borrow_mut().callbacks.request_frame = Some(request_frame);
+    unsafe { ValidateRect(Some(handle), None).ok().log_err() };
+    Some(0)
+}
+
 #[inline]
 fn parse_char_message(wparam: WPARAM, state_ptr: &Rc<WindowsWindowStatePtr>) -> Option<String> {
     let code_point = wparam.loword();

crates/gpui/src/platform/windows/platform.rs πŸ”—

@@ -28,13 +28,12 @@ use windows::{
     core::*,
 };
 
-use crate::{platform::blade::BladeContext, *};
+use crate::*;
 
 pub(crate) struct WindowsPlatform {
     state: RefCell<WindowsPlatformState>,
     raw_window_handles: RwLock<SmallVec<[HWND; 4]>>,
     // The below members will never change throughout the entire lifecycle of the app.
-    gpu_context: BladeContext,
     icon: HICON,
     main_receiver: flume::Receiver<Runnable>,
     background_executor: BackgroundExecutor,
@@ -111,13 +110,11 @@ impl WindowsPlatform {
         let icon = load_icon().unwrap_or_default();
         let state = RefCell::new(WindowsPlatformState::new());
         let raw_window_handles = RwLock::new(SmallVec::new());
-        let gpu_context = BladeContext::new().context("Unable to init GPU context")?;
         let windows_version = WindowsVersion::new().context("Error retrieve windows version")?;
 
         Ok(Self {
             state,
             raw_window_handles,
-            gpu_context,
             icon,
             main_receiver,
             background_executor,
@@ -343,27 +340,11 @@ impl Platform for WindowsPlatform {
 
     fn run(&self, on_finish_launching: Box<dyn 'static + FnOnce()>) {
         on_finish_launching();
-        let vsync_event = unsafe { Owned::new(CreateEventW(None, false, false, None).unwrap()) };
-        begin_vsync(*vsync_event);
-        'a: loop {
-            let wait_result = unsafe {
-                MsgWaitForMultipleObjects(Some(&[*vsync_event]), false, INFINITE, QS_ALLINPUT)
-            };
-
-            match wait_result {
-                // compositor clock ticked so we should draw a frame
-                WAIT_EVENT(0) => self.redraw_all(),
-                // Windows thread messages are posted
-                WAIT_EVENT(1) => {
-                    if self.handle_events() {
-                        break 'a;
-                    }
-                }
-                _ => {
-                    log::error!("Something went wrong while waiting {:?}", wait_result);
-                    break;
-                }
+        loop {
+            if self.handle_events() {
+                break;
             }
+            self.redraw_all();
         }
 
         if let Some(ref mut callback) = self.state.borrow_mut().callbacks.quit {
@@ -455,12 +436,7 @@ impl Platform for WindowsPlatform {
         handle: AnyWindowHandle,
         options: WindowParams,
     ) -> Result<Box<dyn PlatformWindow>> {
-        let window = WindowsWindow::new(
-            handle,
-            options,
-            self.generate_creation_info(),
-            &self.gpu_context,
-        )?;
+        let window = WindowsWindow::new(handle, options, self.generate_creation_info())?;
         let handle = window.get_raw_handle();
         self.raw_window_handles.write().push(handle);
 
@@ -846,16 +822,6 @@ fn file_save_dialog(directory: PathBuf, window: Option<HWND>) -> Result<Option<P
     Ok(Some(PathBuf::from(file_path_string)))
 }
 
-fn begin_vsync(vsync_event: HANDLE) {
-    let event: SafeHandle = vsync_event.into();
-    std::thread::spawn(move || unsafe {
-        loop {
-            windows::Win32::Graphics::Dwm::DwmFlush().log_err();
-            SetEvent(*event).log_err();
-        }
-    });
-}
-
 fn load_icon() -> Result<HICON> {
     let module = unsafe { GetModuleHandleW(None).context("unable to get module handle")? };
     let handle = unsafe {

crates/gpui/src/platform/windows/shaders.hlsl πŸ”—

@@ -0,0 +1,1160 @@
+cbuffer GlobalParams: register(b0) {
+    float2 global_viewport_size;
+    uint2 _global_pad;
+};
+
+Texture2D<float4> t_sprite: register(t0);
+SamplerState s_sprite: register(s0);
+
+struct Bounds {
+    float2 origin;
+    float2 size;
+};
+
+struct Corners {
+    float top_left;
+    float top_right;
+    float bottom_right;
+    float bottom_left;
+};
+
+struct Edges {
+    float top;
+    float right;
+    float bottom;
+    float left;
+};
+
+struct Hsla {
+    float h;
+    float s;
+    float l;
+    float a;
+};
+
+struct LinearColorStop {
+    Hsla color;
+    float percentage;
+};
+
+struct Background {
+    // 0u is Solid
+    // 1u is LinearGradient
+    // 2u is PatternSlash
+    uint tag;
+    // 0u is sRGB linear color
+    // 1u is Oklab color
+    uint color_space;
+    Hsla solid;
+    float gradient_angle_or_pattern_height;
+    LinearColorStop colors[2];
+    uint pad;
+};
+
+struct GradientColor {
+  float4 solid;
+  float4 color0;
+  float4 color1;
+};
+
+struct AtlasTextureId {
+    uint index;
+    uint kind;
+};
+
+struct AtlasBounds {
+    int2 origin;
+    int2 size;
+};
+
+struct AtlasTile {
+    AtlasTextureId texture_id;
+    uint tile_id;
+    uint padding;
+    AtlasBounds bounds;
+};
+
+struct TransformationMatrix {
+    float2x2 rotation_scale;
+    float2 translation;
+};
+
+static const float M_PI_F = 3.141592653f;
+static const float3 GRAYSCALE_FACTORS = float3(0.2126f, 0.7152f, 0.0722f);
+
+float4 to_device_position_impl(float2 position) {
+    float2 device_position = position / global_viewport_size * float2(2.0, -2.0) + float2(-1.0, 1.0);
+    return float4(device_position, 0., 1.);
+}
+
+float4 to_device_position(float2 unit_vertex, Bounds bounds) {
+    float2 position = unit_vertex * bounds.size + bounds.origin;
+    return to_device_position_impl(position);
+}
+
+float4 distance_from_clip_rect_impl(float2 position, Bounds clip_bounds) {
+    float2 tl = position - clip_bounds.origin;
+    float2 br = clip_bounds.origin + clip_bounds.size - position;
+    return float4(tl.x, br.x, tl.y, br.y);
+}
+
+float4 distance_from_clip_rect(float2 unit_vertex, Bounds bounds, Bounds clip_bounds) {
+    float2 position = unit_vertex * bounds.size + bounds.origin;
+    return distance_from_clip_rect_impl(position, clip_bounds);
+}
+
+// Convert linear RGB to sRGB
+float3 linear_to_srgb(float3 color) {
+    return pow(color, float3(2.2, 2.2, 2.2));
+}
+
+// Convert sRGB to linear RGB
+float3 srgb_to_linear(float3 color) {
+    return pow(color, float3(1.0 / 2.2, 1.0 / 2.2, 1.0 / 2.2));
+}
+
+/// Hsla to linear RGBA conversion.
+float4 hsla_to_rgba(Hsla hsla) {
+    float h = hsla.h * 6.0; // Now, it's an angle but scaled in [0, 6) range
+    float s = hsla.s;
+    float l = hsla.l;
+    float a = hsla.a;
+
+    float c = (1.0 - abs(2.0 * l - 1.0)) * s;
+    float x = c * (1.0 - abs(fmod(h, 2.0) - 1.0));
+    float m = l - c / 2.0;
+
+    float r = 0.0;
+    float g = 0.0;
+    float b = 0.0;
+
+    if (h >= 0.0 && h < 1.0) {
+        r = c;
+        g = x;
+        b = 0.0;
+    } else if (h >= 1.0 && h < 2.0) {
+        r = x;
+        g = c;
+        b = 0.0;
+    } else if (h >= 2.0 && h < 3.0) {
+        r = 0.0;
+        g = c;
+        b = x;
+    } else if (h >= 3.0 && h < 4.0) {
+        r = 0.0;
+        g = x;
+        b = c;
+    } else if (h >= 4.0 && h < 5.0) {
+        r = x;
+        g = 0.0;
+        b = c;
+    } else {
+        r = c;
+        g = 0.0;
+        b = x;
+    }
+
+    float4 rgba;
+    rgba.x = (r + m);
+    rgba.y = (g + m);
+    rgba.z = (b + m);
+    rgba.w = a;
+    return rgba;
+}
+
+// Converts a sRGB color to the Oklab color space.
+// Reference: https://bottosson.github.io/posts/oklab/#converting-from-linear-srgb-to-oklab
+float4 srgb_to_oklab(float4 color) {
+    // Convert non-linear sRGB to linear sRGB
+    color = float4(srgb_to_linear(color.rgb), color.a);
+
+    float l = 0.4122214708 * color.r + 0.5363325363 * color.g + 0.0514459929 * color.b;
+    float m = 0.2119034982 * color.r + 0.6806995451 * color.g + 0.1073969566 * color.b;
+    float s = 0.0883024619 * color.r + 0.2817188376 * color.g + 0.6299787005 * color.b;
+
+    float l_ = pow(l, 1.0/3.0);
+    float m_ = pow(m, 1.0/3.0);
+    float s_ = pow(s, 1.0/3.0);
+
+    return float4(
+        0.2104542553 * l_ + 0.7936177850 * m_ - 0.0040720468 * s_,
+        1.9779984951 * l_ - 2.4285922050 * m_ + 0.4505937099 * s_,
+        0.0259040371 * l_ + 0.7827717662 * m_ - 0.8086757660 * s_,
+        color.a
+    );
+}
+
+// Converts an Oklab color to the sRGB color space.
+float4 oklab_to_srgb(float4 color) {
+    float l_ = color.r + 0.3963377774 * color.g + 0.2158037573 * color.b;
+    float m_ = color.r - 0.1055613458 * color.g - 0.0638541728 * color.b;
+    float s_ = color.r - 0.0894841775 * color.g - 1.2914855480 * color.b;
+
+    float l = l_ * l_ * l_;
+    float m = m_ * m_ * m_;
+    float s = s_ * s_ * s_;
+
+    float3 linear_rgb = float3(
+        4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,
+        -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,
+        -0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s
+    );
+
+    // Convert linear sRGB to non-linear sRGB
+    return float4(linear_to_srgb(linear_rgb), color.a);
+}
+
+// This approximates the error function, needed for the gaussian integral
+float2 erf(float2 x) {
+    float2 s = sign(x);
+    float2 a = abs(x);
+    x = 1. + (0.278393 + (0.230389 + 0.078108 * (a * a)) * a) * a;
+    x *= x;
+    return s - s / (x * x);
+}
+
+float blur_along_x(float x, float y, float sigma, float corner, float2 half_size) {
+    float delta = min(half_size.y - corner - abs(y), 0.);
+    float curved = half_size.x - corner + sqrt(max(0., corner * corner - delta * delta));
+    float2 integral = 0.5 + 0.5 * erf((x + float2(-curved, curved)) * (sqrt(0.5) / sigma));
+    return integral.y - integral.x;
+}
+
+// A standard gaussian function, used for weighting samples
+float gaussian(float x, float sigma) {
+    return exp(-(x * x) / (2. * sigma * sigma)) / (sqrt(2. * M_PI_F) * sigma);
+}
+
+float4 over(float4 below, float4 above) {
+    float4 result;
+    float alpha = above.a + below.a * (1.0 - above.a);
+    result.rgb = (above.rgb * above.a + below.rgb * below.a * (1.0 - above.a)) / alpha;
+    result.a = alpha;
+    return result;
+}
+
+float2 to_tile_position(float2 unit_vertex, AtlasTile tile) {
+    float2 atlas_size;
+    t_sprite.GetDimensions(atlas_size.x, atlas_size.y);
+    return (float2(tile.bounds.origin) + unit_vertex * float2(tile.bounds.size)) / atlas_size;
+}
+
+// Selects corner radius based on quadrant.
+float pick_corner_radius(float2 center_to_point, Corners corner_radii) {
+    if (center_to_point.x < 0.) {
+        if (center_to_point.y < 0.) {
+            return corner_radii.top_left;
+        } else {
+            return corner_radii.bottom_left;
+        }
+    } else {
+        if (center_to_point.y < 0.) {
+            return corner_radii.top_right;
+        } else {
+            return corner_radii.bottom_right;
+        }
+    }
+}
+
+float4 to_device_position_transformed(float2 unit_vertex, Bounds bounds,
+                                      TransformationMatrix transformation) {
+    float2 position = unit_vertex * bounds.size + bounds.origin;
+    float2 transformed = mul(position, transformation.rotation_scale) + transformation.translation;
+    float2 device_position = transformed / global_viewport_size * float2(2.0, -2.0) + float2(-1.0, 1.0);
+    return float4(device_position, 0.0, 1.0);
+}
+
+// Implementation of quad signed distance field
+float quad_sdf_impl(float2 corner_center_to_point, float corner_radius) {
+    if (corner_radius == 0.0) {
+        // Fast path for unrounded corners
+        return max(corner_center_to_point.x, corner_center_to_point.y);
+    } else {
+        // Signed distance of the point from a quad that is inset by corner_radius
+        // It is negative inside this quad, and positive outside
+        float signed_distance_to_inset_quad =
+            // 0 inside the inset quad, and positive outside
+            length(max(float2(0.0, 0.0), corner_center_to_point)) +
+            // 0 outside the inset quad, and negative inside
+            min(0.0, max(corner_center_to_point.x, corner_center_to_point.y));
+
+        return signed_distance_to_inset_quad - corner_radius;
+    }
+}
+
+float quad_sdf(float2 pt, Bounds bounds, Corners corner_radii) {
+    float2 half_size = bounds.size / 2.;
+    float2 center = bounds.origin + half_size;
+    float2 center_to_point = pt - center;
+    float corner_radius = pick_corner_radius(center_to_point, corner_radii);
+    float2 corner_to_point = abs(center_to_point) - half_size;
+    float2 corner_center_to_point = corner_to_point + corner_radius;
+    return quad_sdf_impl(corner_center_to_point, corner_radius);
+}
+
+GradientColor prepare_gradient_color(uint tag, uint color_space, Hsla solid, LinearColorStop colors[2]) {
+    GradientColor output;
+    if (tag == 0 || tag == 2) {
+        output.solid = hsla_to_rgba(solid);
+    } else if (tag == 1) {
+        output.color0 = hsla_to_rgba(colors[0].color);
+        output.color1 = hsla_to_rgba(colors[1].color);
+
+        // Prepare color space in vertex for avoid conversion
+        // in fragment shader for performance reasons
+        if (color_space == 1) {
+            // Oklab
+            output.color0 = srgb_to_oklab(output.color0);
+            output.color1 = srgb_to_oklab(output.color1);
+        }
+    }
+
+    return output;
+}
+
+float2x2 rotate2d(float angle) {
+    float s = sin(angle);
+    float c = cos(angle);
+    return float2x2(c, -s, s, c);
+}
+
+float4 gradient_color(Background background,
+                      float2 position,
+                      Bounds bounds,
+                      float4 solid_color, float4 color0, float4 color1) {
+    float4 color;
+
+    switch (background.tag) {
+        case 0:
+            color = solid_color;
+            break;
+        case 1: {
+            // -90 degrees to match the CSS gradient angle.
+            float gradient_angle = background.gradient_angle_or_pattern_height;
+            float radians = (fmod(gradient_angle, 360.0) - 90.0) * (M_PI_F / 180.0);
+            float2 direction = float2(cos(radians), sin(radians));
+
+            // Expand the short side to be the same as the long side
+            if (bounds.size.x > bounds.size.y) {
+                direction.y *= bounds.size.y / bounds.size.x;
+            } else {
+                direction.x *=  bounds.size.x / bounds.size.y;
+            }
+
+            // Get the t value for the linear gradient with the color stop percentages.
+            float2 half_size = bounds.size * 0.5;
+            float2 center = bounds.origin + half_size;
+            float2 center_to_point = position - center;
+            float t = dot(center_to_point, direction) / length(direction);
+            // Check the direct to determine the use x or y
+            if (abs(direction.x) > abs(direction.y)) {
+                t = (t + half_size.x) / bounds.size.x;
+            } else {
+                t = (t + half_size.y) / bounds.size.y;
+            }
+
+            // Adjust t based on the stop percentages
+            t = (t - background.colors[0].percentage)
+                / (background.colors[1].percentage
+                - background.colors[0].percentage);
+            t = clamp(t, 0.0, 1.0);
+
+            switch (background.color_space) {
+                case 0:
+                    color = lerp(color0, color1, t);
+                    break;
+                case 1: {
+                    float4 oklab_color = lerp(color0, color1, t);
+                    color = oklab_to_srgb(oklab_color);
+                    break;
+                }
+            }
+            break;
+        }
+        case 2: {
+            float gradient_angle_or_pattern_height = background.gradient_angle_or_pattern_height;
+            float pattern_width = (gradient_angle_or_pattern_height / 65535.0f) / 255.0f;
+            float pattern_interval = fmod(gradient_angle_or_pattern_height, 65535.0f) / 255.0f;
+            float pattern_height = pattern_width + pattern_interval;
+            float stripe_angle = M_PI_F / 4.0;
+            float pattern_period = pattern_height * sin(stripe_angle);
+            float2x2 rotation = rotate2d(stripe_angle);
+            float2 relative_position = position - bounds.origin;
+            float2 rotated_point = mul(rotation, relative_position);
+            float pattern = fmod(rotated_point.x, pattern_period);
+            float distance = min(pattern, pattern_period - pattern) - pattern_period * (pattern_width / pattern_height) /  2.0f;
+            color = solid_color;
+            color.a *= saturate(0.5 - distance);
+            break;
+        }
+    }
+
+    return color;
+}
+
+// Returns the dash velocity of a corner given the dash velocity of the two
+// sides, by returning the slower velocity (larger dashes).
+//
+// Since 0 is used for dash velocity when the border width is 0 (instead of
+// +inf), this returns the other dash velocity in that case.
+//
+// An alternative to this might be to appropriately interpolate the dash
+// velocity around the corner, but that seems overcomplicated.
+float corner_dash_velocity(float dv1, float dv2) {
+    if (dv1 == 0.0) {
+        return dv2;
+    } else if (dv2 == 0.0) {
+        return dv1;
+    } else {
+        return min(dv1, dv2);
+    }
+}
+
+// Returns alpha used to render antialiased dashes.
+// `t` is within the dash when `fmod(t, period) < length`.
+float dash_alpha(
+    float t, float period, float length, float dash_velocity,
+    float antialias_threshold
+) {
+    float half_period = period / 2.0;
+    float half_length = length / 2.0;
+    // Value in [-half_period, half_period]
+    // The dash is in [-half_length, half_length]
+    float centered = fmod(t + half_period - half_length, period) - half_period;
+    // Signed distance for the dash, negative values are inside the dash
+    float signed_distance = abs(centered) - half_length;
+    // Antialiased alpha based on the signed distance
+    return saturate(antialias_threshold - signed_distance / dash_velocity);
+}
+
+// This approximates distance to the nearest point to a quarter ellipse in a way
+// that is sufficient for anti-aliasing when the ellipse is not very eccentric.
+// The components of `point` are expected to be positive.
+//
+// Negative on the outside and positive on the inside.
+float quarter_ellipse_sdf(float2 pt, float2 radii) {
+    // Scale the space to treat the ellipse like a unit circle
+    float2 circle_vec = pt / radii;
+    float unit_circle_sdf = length(circle_vec) - 1.0;
+    // Approximate up-scaling of the length by using the average of the radii.
+    //
+    // TODO: A better solution would be to use the gradient of the implicit
+    // function for an ellipse to approximate a scaling factor.
+    return unit_circle_sdf * (radii.x + radii.y) * -0.5;
+}
+
+/*
+**
+**              Quads
+**
+*/
+
+struct Quad {
+    uint order;
+    uint border_style;
+    Bounds bounds;
+    Bounds content_mask;
+    Background background;
+    Hsla border_color;
+    Corners corner_radii;
+    Edges border_widths;
+};
+
+struct QuadVertexOutput {
+    nointerpolation uint quad_id: TEXCOORD0;
+    float4 position: SV_Position;
+    nointerpolation float4 border_color: COLOR0;
+    nointerpolation float4 background_solid: COLOR1;
+    nointerpolation float4 background_color0: COLOR2;
+    nointerpolation float4 background_color1: COLOR3;
+    float4 clip_distance: SV_ClipDistance;
+};
+
+struct QuadFragmentInput {
+    nointerpolation uint quad_id: TEXCOORD0;
+    float4 position: SV_Position;
+    nointerpolation float4 border_color: COLOR0;
+    nointerpolation float4 background_solid: COLOR1;
+    nointerpolation float4 background_color0: COLOR2;
+    nointerpolation float4 background_color1: COLOR3;
+};
+
+StructuredBuffer<Quad> quads: register(t1);
+
+QuadVertexOutput quad_vertex(uint vertex_id: SV_VertexID, uint quad_id: SV_InstanceID) {
+    float2 unit_vertex = float2(float(vertex_id & 1u), 0.5 * float(vertex_id & 2u));
+    Quad quad = quads[quad_id];
+    float4 device_position = to_device_position(unit_vertex, quad.bounds);
+
+    GradientColor gradient = prepare_gradient_color(
+        quad.background.tag,
+        quad.background.color_space,
+        quad.background.solid,
+        quad.background.colors
+    );
+    float4 clip_distance = distance_from_clip_rect(unit_vertex, quad.bounds, quad.content_mask);
+    float4 border_color = hsla_to_rgba(quad.border_color);
+
+    QuadVertexOutput output;
+    output.position = device_position;
+    output.border_color = border_color;
+    output.quad_id = quad_id;
+    output.background_solid = gradient.solid;
+    output.background_color0 = gradient.color0;
+    output.background_color1 = gradient.color1;
+    output.clip_distance = clip_distance;
+    return output;
+}
+
+float4 quad_fragment(QuadFragmentInput input): SV_Target {
+    Quad quad = quads[input.quad_id];
+    float4 background_color = gradient_color(quad.background, input.position.xy, quad.bounds,
+    input.background_solid, input.background_color0, input.background_color1);
+
+    bool unrounded = quad.corner_radii.top_left == 0.0 &&
+        quad.corner_radii.top_right == 0.0 &&
+        quad.corner_radii.bottom_left == 0.0 &&
+        quad.corner_radii.bottom_right == 0.0;
+
+    // Fast path when the quad is not rounded and doesn't have any border
+    if (quad.border_widths.top == 0.0 &&
+        quad.border_widths.left == 0.0 &&
+        quad.border_widths.right == 0.0 &&
+        quad.border_widths.bottom == 0.0 &&
+        unrounded) {
+        return background_color;
+    }
+
+    float2 size = quad.bounds.size;
+    float2 half_size = size / 2.;
+    float2 the_point = input.position.xy - quad.bounds.origin;
+    float2 center_to_point = the_point - half_size;
+
+    // Signed distance field threshold for inclusion of pixels. 0.5 is the
+    // minimum distance between the center of the pixel and the edge.
+    const float antialias_threshold = 0.5;
+
+    // Radius of the nearest corner
+    float corner_radius = pick_corner_radius(center_to_point, quad.corner_radii);
+
+    float2 border = float2(
+        center_to_point.x < 0.0 ? quad.border_widths.left : quad.border_widths.right,
+        center_to_point.y < 0.0 ? quad.border_widths.top : quad.border_widths.bottom
+    );
+
+    // 0-width borders are reduced so that `inner_sdf >= antialias_threshold`.
+    // The purpose of this is to not draw antialiasing pixels in this case.
+    float2 reduced_border = float2(
+        border.x == 0.0 ? -antialias_threshold : border.x,
+        border.y == 0.0 ? -antialias_threshold : border.y
+    );
+
+    // Vector from the corner of the quad bounds to the point, after mirroring
+    // the point into the bottom right quadrant. Both components are <= 0.
+    float2 corner_to_point = abs(center_to_point) - half_size;
+
+    // Vector from the point to the center of the rounded corner's circle, also
+    // mirrored into bottom right quadrant.
+    float2 corner_center_to_point = corner_to_point + corner_radius;
+
+    // Whether the nearest point on the border is rounded
+    bool is_near_rounded_corner =
+        corner_center_to_point.x >= 0.0 &&
+        corner_center_to_point.y >= 0.0;
+
+    // Vector from straight border inner corner to point.
+    //
+    // 0-width borders are turned into width -1 so that inner_sdf is > 1.0 near
+    // the border. Without this, antialiasing pixels would be drawn.
+    float2 straight_border_inner_corner_to_point = corner_to_point + reduced_border;
+
+    // Whether the point is beyond the inner edge of the straight border
+    bool is_beyond_inner_straight_border =
+        straight_border_inner_corner_to_point.x > 0.0 ||
+        straight_border_inner_corner_to_point.y > 0.0;
+
+    // Whether the point is far enough inside the quad, such that the pixels are
+    // not affected by the straight border.
+    bool is_within_inner_straight_border =
+        straight_border_inner_corner_to_point.x < -antialias_threshold &&
+        straight_border_inner_corner_to_point.y < -antialias_threshold;
+
+    // Fast path for points that must be part of the background
+    if (is_within_inner_straight_border && !is_near_rounded_corner) {
+        return background_color;
+    }
+
+    // Signed distance of the point to the outside edge of the quad's border
+    float outer_sdf = quad_sdf_impl(corner_center_to_point, corner_radius);
+
+    // Approximate signed distance of the point to the inside edge of the quad's
+    // border. It is negative outside this edge (within the border), and
+    // positive inside.
+    //
+    // This is not always an accurate signed distance:
+    // * The rounded portions with varying border width use an approximation of
+    //   nearest-point-on-ellipse.
+    // * When it is quickly known to be outside the edge, -1.0 is used.
+    float inner_sdf = 0.0;
+    if (corner_center_to_point.x <= 0.0 || corner_center_to_point.y <= 0.0) {
+        // Fast paths for straight borders
+        inner_sdf = -max(straight_border_inner_corner_to_point.x,
+                        straight_border_inner_corner_to_point.y);
+    } else if (is_beyond_inner_straight_border) {
+        // Fast path for points that must be outside the inner edge
+        inner_sdf = -1.0;
+    } else if (reduced_border.x == reduced_border.y) {
+        // Fast path for circular inner edge.
+        inner_sdf = -(outer_sdf + reduced_border.x);
+    } else {
+        float2 ellipse_radii = max(float2(0.0, 0.0), float2(corner_radius, corner_radius) - reduced_border);
+        inner_sdf = quarter_ellipse_sdf(corner_center_to_point, ellipse_radii);
+    }
+
+    // Negative when inside the border
+    float border_sdf = max(inner_sdf, outer_sdf);
+
+    float4 color = background_color;
+    if (border_sdf < antialias_threshold) {
+        float4 border_color = input.border_color;
+        // Dashed border logic when border_style == 1
+        if (quad.border_style == 1) {
+            // Position along the perimeter in "dash space", where each dash
+            // period has length 1
+            float t = 0.0;
+
+            // Total number of dash periods, so that the dash spacing can be
+            // adjusted to evenly divide it
+            float max_t = 0.0;
+
+            // Border width is proportional to dash size. This is the behavior
+            // used by browsers, but also avoids dashes from different segments
+            // overlapping when dash size is smaller than the border width.
+            //
+            // Dash pattern: (2 * border width) dash, (1 * border width) gap
+            const float dash_length_per_width = 2.0;
+            const float dash_gap_per_width = 1.0;
+            const float dash_period_per_width = dash_length_per_width + dash_gap_per_width;
+
+            // Since the dash size is determined by border width, the density of
+            // dashes varies. Multiplying a pixel distance by this returns a
+            // position in dash space - it has units (dash period / pixels). So
+            // a dash velocity of (1 / 10) is 1 dash every 10 pixels.
+            float dash_velocity = 0.0;
+
+            // Dividing this by the border width gives the dash velocity
+            const float dv_numerator = 1.0 / dash_period_per_width;
+
+            if (unrounded) {
+                // When corners aren't rounded, the dashes are separately laid
+                // out on each straight line, rather than around the whole
+                // perimeter. This way each line starts and ends with a dash.
+                bool is_horizontal = corner_center_to_point.x < corner_center_to_point.y;
+                float border_width = is_horizontal ? border.x : border.y;
+                dash_velocity = dv_numerator / border_width;
+                t = is_horizontal ? the_point.x : the_point.y;
+                t *= dash_velocity;
+                max_t = is_horizontal ? size.x : size.y;
+                max_t *= dash_velocity;
+            } else {
+                // When corners are rounded, the dashes are laid out clockwise
+                // around the whole perimeter.
+
+                float r_tr = quad.corner_radii.top_right;
+                float r_br = quad.corner_radii.bottom_right;
+                float r_bl = quad.corner_radii.bottom_left;
+                float r_tl = quad.corner_radii.top_left;
+
+                float w_t = quad.border_widths.top;
+                float w_r = quad.border_widths.right;
+                float w_b = quad.border_widths.bottom;
+                float w_l = quad.border_widths.left;
+
+                // Straight side dash velocities
+                float dv_t = w_t <= 0.0 ? 0.0 : dv_numerator / w_t;
+                float dv_r = w_r <= 0.0 ? 0.0 : dv_numerator / w_r;
+                float dv_b = w_b <= 0.0 ? 0.0 : dv_numerator / w_b;
+                float dv_l = w_l <= 0.0 ? 0.0 : dv_numerator / w_l;
+
+                // Straight side lengths in dash space
+                float s_t = (size.x - r_tl - r_tr) * dv_t;
+                float s_r = (size.y - r_tr - r_br) * dv_r;
+                float s_b = (size.x - r_br - r_bl) * dv_b;
+                float s_l = (size.y - r_bl - r_tl) * dv_l;
+
+                float corner_dash_velocity_tr = corner_dash_velocity(dv_t, dv_r);
+                float corner_dash_velocity_br = corner_dash_velocity(dv_b, dv_r);
+                float corner_dash_velocity_bl = corner_dash_velocity(dv_b, dv_l);
+                float corner_dash_velocity_tl = corner_dash_velocity(dv_t, dv_l);
+
+                // Corner lengths in dash space
+                float c_tr = r_tr * (M_PI_F / 2.0) * corner_dash_velocity_tr;
+                float c_br = r_br * (M_PI_F / 2.0) * corner_dash_velocity_br;
+                float c_bl = r_bl * (M_PI_F / 2.0) * corner_dash_velocity_bl;
+                float c_tl = r_tl * (M_PI_F / 2.0) * corner_dash_velocity_tl;
+
+                // Cumulative dash space upto each segment
+                float upto_tr = s_t;
+                float upto_r = upto_tr + c_tr;
+                float upto_br = upto_r + s_r;
+                float upto_b = upto_br + c_br;
+                float upto_bl = upto_b + s_b;
+                float upto_l = upto_bl + c_bl;
+                float upto_tl = upto_l + s_l;
+                max_t = upto_tl + c_tl;
+
+                if (is_near_rounded_corner) {
+                    float radians = atan2(corner_center_to_point.y, corner_center_to_point.x);
+                    float corner_t = radians * corner_radius;
+
+                    if (center_to_point.x >= 0.0) {
+                        if (center_to_point.y < 0.0) {
+                            dash_velocity = corner_dash_velocity_tr;
+                            // Subtracted because radians is pi/2 to 0 when
+                            // going clockwise around the top right corner,
+                            // since the y axis has been flipped
+                            t = upto_r - corner_t * dash_velocity;
+                        } else {
+                            dash_velocity = corner_dash_velocity_br;
+                            // Added because radians is 0 to pi/2 when going
+                            // clockwise around the bottom-right corner
+                            t = upto_br + corner_t * dash_velocity;
+                        }
+                    } else {
+                        if (center_to_point.y >= 0.0) {
+                            dash_velocity = corner_dash_velocity_bl;
+                            // Subtracted because radians is pi/1 to 0 when
+                            // going clockwise around the bottom-left corner,
+                            // since the x axis has been flipped
+                            t = upto_l - corner_t * dash_velocity;
+                        } else {
+                            dash_velocity = corner_dash_velocity_tl;
+                            // Added because radians is 0 to pi/2 when going
+                            // clockwise around the top-left corner, since both
+                            // axis were flipped
+                            t = upto_tl + corner_t * dash_velocity;
+                        }
+                    }
+                } else {
+                    // Straight borders
+                    bool is_horizontal = corner_center_to_point.x < corner_center_to_point.y;
+                    if (is_horizontal) {
+                        if (center_to_point.y < 0.0) {
+                            dash_velocity = dv_t;
+                            t = (the_point.x - r_tl) * dash_velocity;
+                        } else {
+                            dash_velocity = dv_b;
+                            t = upto_bl - (the_point.x - r_bl) * dash_velocity;
+                        }
+                    } else {
+                        if (center_to_point.x < 0.0) {
+                            dash_velocity = dv_l;
+                            t = upto_tl - (the_point.y - r_tl) * dash_velocity;
+                        } else {
+                            dash_velocity = dv_r;
+                            t = upto_r + (the_point.y - r_tr) * dash_velocity;
+                        }
+                    }
+                }
+            }
+            float dash_length = dash_length_per_width / dash_period_per_width;
+            float desired_dash_gap = dash_gap_per_width / dash_period_per_width;
+
+            // Straight borders should start and end with a dash, so max_t is
+            // reduced to cause this.
+            max_t -= unrounded ? dash_length : 0.0;
+            if (max_t >= 1.0) {
+                // Adjust dash gap to evenly divide max_t
+                float dash_count = floor(max_t);
+                float dash_period = max_t / dash_count;
+                border_color.a *= dash_alpha(t, dash_period, dash_length, dash_velocity, antialias_threshold);
+            } else if (unrounded) {
+                // When there isn't enough space for the full gap between the
+                // two start / end dashes of a straight border, reduce gap to
+                // make them fit.
+                float dash_gap = max_t - dash_length;
+                if (dash_gap > 0.0) {
+                    float dash_period = dash_length + dash_gap;
+                    border_color.a *= dash_alpha(t, dash_period, dash_length, dash_velocity, antialias_threshold);
+                }
+            }
+        }
+
+        // Blend the border on top of the background and then linearly interpolate
+        // between the two as we slide inside the background.
+        float4 blended_border = over(background_color, border_color);
+        color = lerp(background_color, blended_border,
+                    saturate(antialias_threshold - inner_sdf));
+    }
+
+    return color * float4(1.0, 1.0, 1.0, saturate(antialias_threshold - outer_sdf));
+}
+
+/*
+**
+**              Shadows
+**
+*/
+
+struct Shadow {
+    uint order;
+    float blur_radius;
+    Bounds bounds;
+    Corners corner_radii;
+    Bounds content_mask;
+    Hsla color;
+};
+
+struct ShadowVertexOutput {
+    nointerpolation uint shadow_id: TEXCOORD0;
+    float4 position: SV_Position;
+    nointerpolation float4 color: COLOR;
+    float4 clip_distance: SV_ClipDistance;
+};
+
+struct ShadowFragmentInput {
+  nointerpolation uint shadow_id: TEXCOORD0;
+  float4 position: SV_Position;
+  nointerpolation float4 color: COLOR;
+};
+
+StructuredBuffer<Shadow> shadows: register(t1);
+
+ShadowVertexOutput shadow_vertex(uint vertex_id: SV_VertexID, uint shadow_id: SV_InstanceID) {
+    float2 unit_vertex = float2(float(vertex_id & 1u), 0.5 * float(vertex_id & 2u));
+    Shadow shadow = shadows[shadow_id];
+
+    float margin = 3.0 * shadow.blur_radius;
+    Bounds bounds = shadow.bounds;
+    bounds.origin -= margin;
+    bounds.size += 2.0 * margin;
+
+    float4 device_position = to_device_position(unit_vertex, bounds);
+    float4 clip_distance = distance_from_clip_rect(unit_vertex, bounds, shadow.content_mask);
+    float4 color = hsla_to_rgba(shadow.color);
+
+    ShadowVertexOutput output;
+    output.position = device_position;
+    output.color = color;
+    output.shadow_id = shadow_id;
+    output.clip_distance = clip_distance;
+
+    return output;
+}
+
+float4 shadow_fragment(ShadowFragmentInput input): SV_TARGET {
+    Shadow shadow = shadows[input.shadow_id];
+
+    float2 half_size = shadow.bounds.size / 2.;
+    float2 center = shadow.bounds.origin + half_size;
+    float2 point0 = input.position.xy - center;
+    float corner_radius = pick_corner_radius(point0, shadow.corner_radii);
+
+    // The signal is only non-zero in a limited range, so don't waste samples
+    float low = point0.y - half_size.y;
+    float high = point0.y + half_size.y;
+    float start = clamp(-3. * shadow.blur_radius, low, high);
+    float end = clamp(3. * shadow.blur_radius, low, high);
+
+    // Accumulate samples (we can get away with surprisingly few samples)
+    float step = (end - start) / 4.;
+    float y = start + step * 0.5;
+    float alpha = 0.;
+    for (int i = 0; i < 4; i++) {
+        alpha += blur_along_x(point0.x, point0.y - y, shadow.blur_radius,
+                            corner_radius, half_size) *
+                gaussian(y, shadow.blur_radius) * step;
+        y += step;
+    }
+
+    return input.color * float4(1., 1., 1., alpha);
+}
+
+/*
+**
+**              Path Rasterization
+**
+*/
+
+struct PathRasterizationSprite {
+    float2 xy_position;
+    float2 st_position;
+    Background color;
+    Bounds bounds;
+};
+
+StructuredBuffer<PathRasterizationSprite> path_rasterization_sprites: register(t1);
+
+struct PathVertexOutput {
+    float4 position: SV_Position;
+    float2 st_position: TEXCOORD0;
+    nointerpolation uint vertex_id: TEXCOORD1;
+    float4 clip_distance: SV_ClipDistance;
+};
+
+struct PathFragmentInput {
+    float4 position: SV_Position;
+    float2 st_position: TEXCOORD0;
+    nointerpolation uint vertex_id: TEXCOORD1;
+};
+
+PathVertexOutput path_rasterization_vertex(uint vertex_id: SV_VertexID) {
+    PathRasterizationSprite sprite = path_rasterization_sprites[vertex_id];
+
+    PathVertexOutput output;
+    output.position = to_device_position_impl(sprite.xy_position);
+    output.st_position = sprite.st_position;
+    output.vertex_id = vertex_id;
+    output.clip_distance = distance_from_clip_rect_impl(sprite.xy_position, sprite.bounds);
+
+    return output;
+}
+
+float4 path_rasterization_fragment(PathFragmentInput input): SV_Target {
+    float2 dx = ddx(input.st_position);
+    float2 dy = ddy(input.st_position);
+    PathRasterizationSprite sprite = path_rasterization_sprites[input.vertex_id];
+    
+    Background background = sprite.color;
+    Bounds bounds = sprite.bounds;
+
+    float alpha;
+    if (length(float2(dx.x, dy.x))) {
+        alpha = 1.0;
+    } else {
+        float2 gradient = 2.0 * input.st_position.xx * float2(dx.x, dy.x) - float2(dx.y, dy.y);
+        float f = input.st_position.x * input.st_position.x - input.st_position.y;
+        float distance = f / length(gradient);
+        alpha = saturate(0.5 - distance);
+    }
+
+    GradientColor gradient = prepare_gradient_color(
+        background.tag, background.color_space, background.solid, background.colors);
+
+    float4 color = gradient_color(background, input.position.xy, bounds,
+        gradient.solid, gradient.color0, gradient.color1);
+    return float4(color.rgb * color.a * alpha, alpha * color.a);
+}
+
+/*
+**
+**              Path Sprites
+**
+*/
+
+struct PathSprite {
+    Bounds bounds;
+};
+
+struct PathSpriteVertexOutput {
+    float4 position: SV_Position;
+    float2 texture_coords: TEXCOORD0;
+};
+
+StructuredBuffer<PathSprite> path_sprites: register(t1);
+
+PathSpriteVertexOutput path_sprite_vertex(uint vertex_id: SV_VertexID, uint sprite_id: SV_InstanceID) {
+    float2 unit_vertex = float2(float(vertex_id & 1u), 0.5 * float(vertex_id & 2u));
+    PathSprite sprite = path_sprites[sprite_id];
+
+    // Don't apply content mask because it was already accounted for when rasterizing the path
+    float4 device_position = to_device_position(unit_vertex, sprite.bounds);
+
+    float2 screen_position = sprite.bounds.origin + unit_vertex * sprite.bounds.size;
+    float2 texture_coords = screen_position / global_viewport_size;
+
+    PathSpriteVertexOutput output;
+    output.position = device_position;
+    output.texture_coords = texture_coords;
+    return output;
+}
+
+float4 path_sprite_fragment(PathSpriteVertexOutput input): SV_Target {
+    return t_sprite.Sample(s_sprite, input.texture_coords);
+}
+
+/*
+**
+**              Underlines
+**
+*/
+
+struct Underline {
+    uint order;
+    uint pad;
+    Bounds bounds;
+    Bounds content_mask;
+    Hsla color;
+    float thickness;
+    uint wavy;
+};
+
+struct UnderlineVertexOutput {
+  nointerpolation uint underline_id: TEXCOORD0;
+  float4 position: SV_Position;
+  nointerpolation float4 color: COLOR;
+  float4 clip_distance: SV_ClipDistance;
+};
+
+struct UnderlineFragmentInput {
+  nointerpolation uint underline_id: TEXCOORD0;
+  float4 position: SV_Position;
+  nointerpolation float4 color: COLOR;
+};
+
+StructuredBuffer<Underline> underlines: register(t1);
+
+UnderlineVertexOutput underline_vertex(uint vertex_id: SV_VertexID, uint underline_id: SV_InstanceID) {
+    float2 unit_vertex = float2(float(vertex_id & 1u), 0.5 * float(vertex_id & 2u));
+    Underline underline = underlines[underline_id];
+    float4 device_position = to_device_position(unit_vertex, underline.bounds);
+    float4 clip_distance = distance_from_clip_rect(unit_vertex, underline.bounds,
+                                                    underline.content_mask);
+    float4 color = hsla_to_rgba(underline.color);
+
+    UnderlineVertexOutput output;
+    output.position = device_position;
+    output.color = color;
+    output.underline_id = underline_id;
+    output.clip_distance = clip_distance;
+    return output;
+}
+
+float4 underline_fragment(UnderlineFragmentInput input): SV_Target {
+    Underline underline = underlines[input.underline_id];
+    if (underline.wavy) {
+        float half_thickness = underline.thickness * 0.5;
+        float2 origin = underline.bounds.origin;
+        float2 st = ((input.position.xy - origin) / underline.bounds.size.y) - float2(0., 0.5);
+        float frequency = (M_PI_F * (3. * underline.thickness)) / 8.;
+        float amplitude = 1. / (2. * underline.thickness);
+        float sine = sin(st.x * frequency) * amplitude;
+        float dSine = cos(st.x * frequency) * amplitude * frequency;
+        float distance = (st.y - sine) / sqrt(1. + dSine * dSine);
+        float distance_in_pixels = distance * underline.bounds.size.y;
+        float distance_from_top_border = distance_in_pixels - half_thickness;
+        float distance_from_bottom_border = distance_in_pixels + half_thickness;
+        float alpha = saturate(
+            0.5 - max(-distance_from_bottom_border, distance_from_top_border));
+        return input.color * float4(1., 1., 1., alpha);
+    } else {
+        return input.color;
+    }
+}
+
+/*
+**
+**              Monochrome sprites
+**
+*/
+
+struct MonochromeSprite {
+    uint order;
+    uint pad;
+    Bounds bounds;
+    Bounds content_mask;
+    Hsla color;
+    AtlasTile tile;
+    TransformationMatrix transformation;
+};
+
+struct MonochromeSpriteVertexOutput {
+    float4 position: SV_Position;
+    float2 tile_position: POSITION;
+    nointerpolation float4 color: COLOR;
+    float4 clip_distance: SV_ClipDistance;
+};
+
+struct MonochromeSpriteFragmentInput {
+    float4 position: SV_Position;
+    float2 tile_position: POSITION;
+    nointerpolation float4 color: COLOR;
+};
+
+StructuredBuffer<MonochromeSprite> mono_sprites: register(t1);
+
+MonochromeSpriteVertexOutput monochrome_sprite_vertex(uint vertex_id: SV_VertexID, uint sprite_id: SV_InstanceID) {
+    float2 unit_vertex = float2(float(vertex_id & 1u), 0.5 * float(vertex_id & 2u));
+    MonochromeSprite sprite = mono_sprites[sprite_id];
+    float4 device_position =
+        to_device_position_transformed(unit_vertex, sprite.bounds, sprite.transformation);
+    float4 clip_distance = distance_from_clip_rect(unit_vertex, sprite.bounds, sprite.content_mask);
+    float2 tile_position = to_tile_position(unit_vertex, sprite.tile);
+    float4 color = hsla_to_rgba(sprite.color);
+
+    MonochromeSpriteVertexOutput output;
+    output.position = device_position;
+    output.tile_position = tile_position;
+    output.color = color;
+    output.clip_distance = clip_distance;
+    return output;
+}
+
+float4 monochrome_sprite_fragment(MonochromeSpriteFragmentInput input): SV_Target {
+    float4 sample = t_sprite.Sample(s_sprite, input.tile_position);
+    float4 color = input.color;
+    color.a *= sample.a;
+    return color;
+}
+
+/*
+**
+**              Polychrome sprites
+**
+*/
+
+struct PolychromeSprite {
+    uint order;
+    uint pad;
+    uint grayscale;
+    float opacity;
+    Bounds bounds;
+    Bounds content_mask;
+    Corners corner_radii;
+    AtlasTile tile;
+};
+
+struct PolychromeSpriteVertexOutput {
+    nointerpolation uint sprite_id: TEXCOORD0;
+    float4 position: SV_Position;
+    float2 tile_position: POSITION;
+    float4 clip_distance: SV_ClipDistance;
+};
+
+struct PolychromeSpriteFragmentInput {
+    nointerpolation uint sprite_id: TEXCOORD0;
+    float4 position: SV_Position;
+    float2 tile_position: POSITION;
+};
+
+StructuredBuffer<PolychromeSprite> poly_sprites: register(t1);
+
+PolychromeSpriteVertexOutput polychrome_sprite_vertex(uint vertex_id: SV_VertexID, uint sprite_id: SV_InstanceID) {
+    float2 unit_vertex = float2(float(vertex_id & 1u), 0.5 * float(vertex_id & 2u));
+    PolychromeSprite sprite = poly_sprites[sprite_id];
+    float4 device_position = to_device_position(unit_vertex, sprite.bounds);
+    float4 clip_distance = distance_from_clip_rect(unit_vertex, sprite.bounds,
+                                                    sprite.content_mask);
+    float2 tile_position = to_tile_position(unit_vertex, sprite.tile);
+
+    PolychromeSpriteVertexOutput output;
+    output.position = device_position;
+    output.tile_position = tile_position;
+    output.sprite_id = sprite_id;
+    output.clip_distance = clip_distance;
+    return output;
+}
+
+float4 polychrome_sprite_fragment(PolychromeSpriteFragmentInput input): SV_Target {
+    PolychromeSprite sprite = poly_sprites[input.sprite_id];
+    float4 sample = t_sprite.Sample(s_sprite, input.tile_position);
+    float distance = quad_sdf(input.position.xy, sprite.bounds, sprite.corner_radii);
+
+    float4 color = sample;
+    if ((sprite.grayscale & 0xFFu) != 0u) {
+        float3 grayscale = dot(color.rgb, GRAYSCALE_FACTORS);
+        color = float4(grayscale, sample.a);
+    }
+    color.a *= sprite.opacity * saturate(0.5 - distance);
+    return color;
+}

crates/gpui/src/platform/windows/window.rs πŸ”—

@@ -26,7 +26,6 @@ use windows::{
     core::*,
 };
 
-use crate::platform::blade::{BladeContext, BladeRenderer};
 use crate::*;
 
 pub(crate) struct WindowsWindow(pub Rc<WindowsWindowStatePtr>);
@@ -49,7 +48,7 @@ pub struct WindowsWindowState {
     pub system_key_handled: bool,
     pub hovered: bool,
 
-    pub renderer: BladeRenderer,
+    pub renderer: DirectXRenderer,
 
     pub click_state: ClickState,
     pub system_settings: WindowsSystemSettings,
@@ -80,13 +79,12 @@ pub(crate) struct WindowsWindowStatePtr {
 impl WindowsWindowState {
     fn new(
         hwnd: HWND,
-        transparent: bool,
         cs: &CREATESTRUCTW,
         current_cursor: Option<HCURSOR>,
         display: WindowsDisplay,
-        gpu_context: &BladeContext,
         min_size: Option<Size<Pixels>>,
         appearance: WindowAppearance,
+        disable_direct_composition: bool,
     ) -> Result<Self> {
         let scale_factor = {
             let monitor_dpi = unsafe { GetDpiForWindow(hwnd) } as f32;
@@ -103,7 +101,8 @@ impl WindowsWindowState {
         };
         let border_offset = WindowBorderOffset::default();
         let restore_from_minimized = None;
-        let renderer = windows_renderer::init(gpu_context, hwnd, transparent)?;
+        let renderer = DirectXRenderer::new(hwnd, disable_direct_composition)
+            .context("Creating DirectX renderer")?;
         let callbacks = Callbacks::default();
         let input_handler = None;
         let pending_surrogate = None;
@@ -206,13 +205,12 @@ impl WindowsWindowStatePtr {
     fn new(context: &WindowCreateContext, hwnd: HWND, cs: &CREATESTRUCTW) -> Result<Rc<Self>> {
         let state = RefCell::new(WindowsWindowState::new(
             hwnd,
-            context.transparent,
             cs,
             context.current_cursor,
             context.display,
-            context.gpu_context,
             context.min_size,
             context.appearance,
+            context.disable_direct_composition,
         )?);
 
         Ok(Rc::new_cyclic(|this| Self {
@@ -329,12 +327,11 @@ pub(crate) struct Callbacks {
     pub(crate) appearance_changed: Option<Box<dyn FnMut()>>,
 }
 
-struct WindowCreateContext<'a> {
+struct WindowCreateContext {
     inner: Option<Result<Rc<WindowsWindowStatePtr>>>,
     handle: AnyWindowHandle,
     hide_title_bar: bool,
     display: WindowsDisplay,
-    transparent: bool,
     is_movable: bool,
     min_size: Option<Size<Pixels>>,
     executor: ForegroundExecutor,
@@ -343,9 +340,9 @@ struct WindowCreateContext<'a> {
     drop_target_helper: IDropTargetHelper,
     validation_number: usize,
     main_receiver: flume::Receiver<Runnable>,
-    gpu_context: &'a BladeContext,
     main_thread_id_win32: u32,
     appearance: WindowAppearance,
+    disable_direct_composition: bool,
 }
 
 impl WindowsWindow {
@@ -353,7 +350,6 @@ impl WindowsWindow {
         handle: AnyWindowHandle,
         params: WindowParams,
         creation_info: WindowCreationInfo,
-        gpu_context: &BladeContext,
     ) -> Result<Self> {
         let WindowCreationInfo {
             icon,
@@ -379,14 +375,20 @@ impl WindowsWindow {
                 .map(|title| title.as_ref())
                 .unwrap_or(""),
         );
-        let (dwexstyle, mut dwstyle) = if params.kind == WindowKind::PopUp {
-            (WS_EX_TOOLWINDOW | WS_EX_LAYERED, WINDOW_STYLE(0x0))
+        let disable_direct_composition = std::env::var(DISABLE_DIRECT_COMPOSITION)
+            .is_ok_and(|value| value == "true" || value == "1");
+
+        let (mut dwexstyle, dwstyle) = if params.kind == WindowKind::PopUp {
+            (WS_EX_TOOLWINDOW, WINDOW_STYLE(0x0))
         } else {
             (
-                WS_EX_APPWINDOW | WS_EX_LAYERED,
+                WS_EX_APPWINDOW,
                 WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX,
             )
         };
+        if !disable_direct_composition {
+            dwexstyle |= WS_EX_NOREDIRECTIONBITMAP;
+        }
 
         let hinstance = get_module_handle();
         let display = if let Some(display_id) = params.display_id {
@@ -401,7 +403,6 @@ impl WindowsWindow {
             handle,
             hide_title_bar,
             display,
-            transparent: true,
             is_movable: params.is_movable,
             min_size: params.window_min_size,
             executor,
@@ -410,9 +411,9 @@ impl WindowsWindow {
             drop_target_helper,
             validation_number,
             main_receiver,
-            gpu_context,
             main_thread_id_win32,
             appearance,
+            disable_direct_composition,
         };
         let lpparam = Some(&context as *const _ as *const _);
         let creation_result = unsafe {
@@ -453,14 +454,6 @@ impl WindowsWindow {
                 state: WindowOpenState::Windowed,
             });
         }
-        // The render pipeline will perform compositing on the GPU when the
-        // swapchain is configured correctly (see downstream of
-        // update_transparency).
-        // The following configuration is a one-time setup to ensure that the
-        // window is going to be composited with per-pixel alpha, but the render
-        // pipeline is responsible for effectively calling UpdateLayeredWindow
-        // at the appropriate time.
-        unsafe { SetLayeredWindowAttributes(hwnd, COLORREF(0), 255, LWA_ALPHA)? };
 
         Ok(Self(state_ptr))
     }
@@ -485,7 +478,6 @@ impl rwh::HasDisplayHandle for WindowsWindow {
 
 impl Drop for WindowsWindow {
     fn drop(&mut self) {
-        self.0.state.borrow_mut().renderer.destroy();
         // clone this `Rc` to prevent early release of the pointer
         let this = self.0.clone();
         self.0
@@ -705,24 +697,21 @@ impl PlatformWindow for WindowsWindow {
     }
 
     fn set_background_appearance(&self, background_appearance: WindowBackgroundAppearance) {
-        let mut window_state = self.0.state.borrow_mut();
-        window_state
-            .renderer
-            .update_transparency(background_appearance != WindowBackgroundAppearance::Opaque);
+        let hwnd = self.0.hwnd;
 
         match background_appearance {
             WindowBackgroundAppearance::Opaque => {
                 // ACCENT_DISABLED
-                set_window_composition_attribute(window_state.hwnd, None, 0);
+                set_window_composition_attribute(hwnd, None, 0);
             }
             WindowBackgroundAppearance::Transparent => {
                 // Use ACCENT_ENABLE_TRANSPARENTGRADIENT for transparent background
-                set_window_composition_attribute(window_state.hwnd, None, 2);
+                set_window_composition_attribute(hwnd, None, 2);
             }
             WindowBackgroundAppearance::Blurred => {
                 // Enable acrylic blur
                 // ACCENT_ENABLE_ACRYLICBLURBEHIND
-                set_window_composition_attribute(window_state.hwnd, Some((0, 0, 0, 0)), 4);
+                set_window_composition_attribute(hwnd, Some((0, 0, 0, 0)), 4);
             }
         }
     }
@@ -794,11 +783,11 @@ impl PlatformWindow for WindowsWindow {
     }
 
     fn draw(&self, scene: &Scene) {
-        self.0.state.borrow_mut().renderer.draw(scene)
+        self.0.state.borrow_mut().renderer.draw(scene).log_err();
     }
 
     fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
-        self.0.state.borrow().renderer.sprite_atlas().clone()
+        self.0.state.borrow().renderer.sprite_atlas()
     }
 
     fn get_raw_handle(&self) -> HWND {
@@ -806,11 +795,11 @@ impl PlatformWindow for WindowsWindow {
     }
 
     fn gpu_specs(&self) -> Option<GpuSpecs> {
-        Some(self.0.state.borrow().renderer.gpu_specs())
+        self.0.state.borrow().renderer.gpu_specs().log_err()
     }
 
     fn update_ime_position(&self, _bounds: Bounds<ScaledPixels>) {
-        // todo(windows)
+        // There is no such thing on Windows.
     }
 }
 
@@ -1306,52 +1295,6 @@ fn set_window_composition_attribute(hwnd: HWND, color: Option<Color>, state: u32
     }
 }
 
-mod windows_renderer {
-    use crate::platform::blade::{BladeContext, BladeRenderer, BladeSurfaceConfig};
-    use raw_window_handle as rwh;
-    use std::num::NonZeroIsize;
-    use windows::Win32::{Foundation::HWND, UI::WindowsAndMessaging::GWLP_HINSTANCE};
-
-    use crate::{get_window_long, show_error};
-
-    pub(super) fn init(
-        context: &BladeContext,
-        hwnd: HWND,
-        transparent: bool,
-    ) -> anyhow::Result<BladeRenderer> {
-        let raw = RawWindow { hwnd };
-        let config = BladeSurfaceConfig {
-            size: Default::default(),
-            transparent,
-        };
-        BladeRenderer::new(context, &raw, config)
-            .inspect_err(|err| show_error("Failed to initialize BladeRenderer", err.to_string()))
-    }
-
-    struct RawWindow {
-        hwnd: HWND,
-    }
-
-    impl rwh::HasWindowHandle for RawWindow {
-        fn window_handle(&self) -> Result<rwh::WindowHandle<'_>, rwh::HandleError> {
-            Ok(unsafe {
-                let hwnd = NonZeroIsize::new_unchecked(self.hwnd.0 as isize);
-                let mut handle = rwh::Win32WindowHandle::new(hwnd);
-                let hinstance = get_window_long(self.hwnd, GWLP_HINSTANCE);
-                handle.hinstance = NonZeroIsize::new(hinstance);
-                rwh::WindowHandle::borrow_raw(handle.into())
-            })
-        }
-    }
-
-    impl rwh::HasDisplayHandle for RawWindow {
-        fn display_handle(&self) -> Result<rwh::DisplayHandle<'_>, rwh::HandleError> {
-            let handle = rwh::WindowsDisplayHandle::new();
-            Ok(unsafe { rwh::DisplayHandle::borrow_raw(handle.into()) })
-        }
-    }
-}
-
 #[cfg(test)]
 mod tests {
     use super::ClickState;

crates/gpui/src/window.rs πŸ”—

@@ -1020,7 +1020,7 @@ impl Window {
                     || (active.get()
                         && last_input_timestamp.get().elapsed() < Duration::from_secs(1));
 
-                if invalidator.is_dirty() {
+                if invalidator.is_dirty() || request_frame_options.force_render {
                     measure("frame duration", || {
                         handle
                             .update(&mut cx, |_, window, cx| {

crates/zed/resources/windows/zed.iss πŸ”—

@@ -62,6 +62,7 @@ Source: "{#ResourcesDir}\Zed.exe"; DestDir: "{code:GetInstallDir}"; Flags: ignor
 Source: "{#ResourcesDir}\bin\*"; DestDir: "{code:GetInstallDir}\bin"; Flags: ignoreversion
 Source: "{#ResourcesDir}\tools\*"; DestDir: "{app}\tools"; Flags: ignoreversion
 Source: "{#ResourcesDir}\appx\*"; DestDir: "{app}\appx";  BeforeInstall: RemoveAppxPackage; AfterInstall: AddAppxPackage; Flags: ignoreversion; Check: IsWindows11OrLater
+Source: "{#ResourcesDir}\amd_ags_x64.dll"; DestDir: "{app}"; Flags: ignoreversion
 
 [Icons]
 Name: "{group}\{#AppName}"; Filename: "{app}\{#AppExeName}.exe"; AppUserModelID: "{#AppUserId}"

script/bundle-windows.ps1 πŸ”—

@@ -136,11 +136,22 @@ function SignZedAndItsFriends {
     & "$innoDir\sign.ps1" $files
 }
 
+function DownloadAMDGpuServices {
+    # If you update the AGS SDK version, please also update the version in `crates/gpui/src/platform/windows/directx_renderer.rs`
+    $url = "https://codeload.github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK/zip/refs/tags/v6.3.0"
+    $zipPath = ".\AGS_SDK_v6.3.0.zip"
+    # Download the AGS SDK zip file
+    Invoke-WebRequest -Uri $url -OutFile $zipPath
+    # Extract the AGS SDK zip file
+    Expand-Archive -Path $zipPath -DestinationPath "." -Force
+}
+
 function CollectFiles {
     Move-Item -Path "$innoDir\zed_explorer_command_injector.appx" -Destination "$innoDir\appx\zed_explorer_command_injector.appx" -Force
     Move-Item -Path "$innoDir\zed_explorer_command_injector.dll" -Destination "$innoDir\appx\zed_explorer_command_injector.dll" -Force
     Move-Item -Path "$innoDir\cli.exe" -Destination "$innoDir\bin\zed.exe" -Force
     Move-Item -Path "$innoDir\auto_update_helper.exe" -Destination "$innoDir\tools\auto_update_helper.exe" -Force
+    Move-Item -Path ".\AGS_SDK-6.3.0\ags_lib\lib\amd_ags_x64.dll" -Destination "$innoDir\amd_ags_x64.dll" -Force
 }
 
 function BuildInstaller {
@@ -211,7 +222,6 @@ function BuildInstaller {
     # Windows runner 2022 default has iscc in PATH, https://github.com/actions/runner-images/blob/main/images/windows/Windows2022-Readme.md
     # Currently, we are using Windows 2022 runner.
     # Windows runner 2025 doesn't have iscc in PATH for now, https://github.com/actions/runner-images/issues/11228
-    # $innoSetupPath = "iscc.exe"
     $innoSetupPath = "C:\Program Files (x86)\Inno Setup 6\ISCC.exe"
 
     $definitions = @{
@@ -268,6 +278,7 @@ BuildZedAndItsFriends
 MakeAppx
 SignZedAndItsFriends
 ZipZedAndItsFriendsDebug
+DownloadAMDGpuServices
 CollectFiles
 BuildInstaller
 

tooling/workspace-hack/Cargo.toml πŸ”—

@@ -558,7 +558,6 @@ getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3", default-f
 getrandom-6f8ce4dd05d13bba = { package = "getrandom", version = "0.2", default-features = false, features = ["js", "rdrand"] }
 hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] }
 itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" }
-naga = { version = "25", features = ["spv-out", "wgsl-in"] }
 ring = { version = "0.17", features = ["std"] }
 rustix-d585fab2519d2d1 = { package = "rustix", version = "0.38", features = ["event"] }
 scopeguard = { version = "1" }
@@ -582,7 +581,6 @@ getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3", default-f
 getrandom-6f8ce4dd05d13bba = { package = "getrandom", version = "0.2", default-features = false, features = ["js", "rdrand"] }
 hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] }
 itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" }
-naga = { version = "25", features = ["spv-out", "wgsl-in"] }
 proc-macro2 = { version = "1", default-features = false, features = ["span-locations"] }
 ring = { version = "0.17", features = ["std"] }
 rustix-d585fab2519d2d1 = { package = "rustix", version = "0.38", features = ["event"] }

typos.toml πŸ”—

@@ -71,6 +71,10 @@ extend-ignore-re = [
     # Not an actual typo but an intentionally invalid color, in `color_extractor`
     "#fof",
     # Stripped version of reserved keyword `type`
-    "typ"
+    "typ",
+    # AMD GPU Services
+    "ags",
+    # AMD GPU Services
+    "AGS"
 ]
 check-filename = true