Compare commits
207 commits
main
...
windows/re
Author | SHA1 | Date | |
---|---|---|---|
![]() |
4a3d56749e | ||
![]() |
3519e8fd7c | ||
![]() |
b3372e7eac | ||
![]() |
4f7bb14acf | ||
![]() |
99c5b72b3d | ||
![]() |
c995dd2016 | ||
![]() |
80be0e29b9 | ||
![]() |
b75f6e2210 | ||
![]() |
b8f85be372 | ||
![]() |
34e433ad90 | ||
![]() |
6a91ac26d7 | ||
![]() |
345fd526fc | ||
![]() |
7c8074ce5c | ||
![]() |
0a0803e2a7 | ||
![]() |
8c8b91470a | ||
![]() |
98692cc928 | ||
![]() |
d194bf4f52 | ||
![]() |
cc763729a0 | ||
![]() |
e370c3d601 | ||
![]() |
1d60984cb6 | ||
![]() |
eb3bb95c91 | ||
![]() |
554b36fd3c | ||
![]() |
89a863d012 | ||
![]() |
441731de2e | ||
![]() |
ead7a1e1f0 | ||
![]() |
73eaee8f6f | ||
![]() |
98f31172ab | ||
![]() |
181f324473 | ||
![]() |
a1f03ee42c | ||
![]() |
741b38f906 | ||
![]() |
599b82fc9d | ||
![]() |
64b3b050e3 | ||
![]() |
62d1b7e36f | ||
![]() |
d7b14d8dc5 | ||
![]() |
ce67ce1482 | ||
![]() |
8eea9aad40 | ||
![]() |
e9697e4639 | ||
![]() |
92b0a7e760 | ||
![]() |
d96bafb1e5 | ||
![]() |
5f3a1bdbd1 | ||
![]() |
1ee81a507b | ||
![]() |
89d34e1513 | ||
![]() |
a9058346bf | ||
![]() |
ac1ea0f96d | ||
![]() |
0065e5fd76 | ||
![]() |
ca6aa25d1e | ||
![]() |
6964cecc14 | ||
![]() |
63daf44693 | ||
![]() |
4de2ebf954 | ||
![]() |
3277640f55 | ||
![]() |
d192ac6b7f | ||
![]() |
c67ddd7572 | ||
![]() |
b54eaecbbc | ||
![]() |
2744e6cb65 | ||
![]() |
18937f5756 | ||
![]() |
347b863ac6 | ||
![]() |
9d8ef8156d | ||
![]() |
9dbbee0334 | ||
![]() |
32f2505fbf | ||
![]() |
2711d8823c | ||
![]() |
787fee8a1a | ||
![]() |
be7d56e11b | ||
![]() |
fcb77979f3 | ||
![]() |
787c6382f9 | ||
![]() |
74d953d024 | ||
![]() |
b5377c56f2 | ||
![]() |
275d84d566 | ||
![]() |
3978bba5a7 | ||
![]() |
52c0fa5ce9 | ||
![]() |
d208f75f46 | ||
![]() |
1b0a0aa58e | ||
![]() |
5ff9114b18 | ||
![]() |
d9c6d09545 | ||
![]() |
61981aabb5 | ||
![]() |
0b57c86e07 | ||
![]() |
c7342a9df5 | ||
![]() |
0e45ef7e43 | ||
![]() |
0c40bb9b5f | ||
![]() |
5058752f2d | ||
![]() |
432d11f57b | ||
![]() |
32488e1e2d | ||
![]() |
9acee42c38 | ||
![]() |
72c55b4653 | ||
![]() |
fa1320d9aa | ||
![]() |
eb310bcf7d | ||
![]() |
8c1d9f75d1 | ||
![]() |
499b3b6b50 | ||
![]() |
c6e020f60f | ||
![]() |
7ab2d0d800 | ||
![]() |
c007121b41 | ||
![]() |
22c9d133bd | ||
![]() |
32758022df | ||
![]() |
0d8600bf1e | ||
![]() |
22cba07072 | ||
![]() |
642d769502 | ||
![]() |
bfdcc65801 | ||
![]() |
54e2420405 | ||
![]() |
b012246d2b | ||
![]() |
667c19907a | ||
![]() |
5261c02d18 | ||
![]() |
204071e6bf | ||
![]() |
5472c71f1a | ||
![]() |
723712e3cf | ||
![]() |
0c274370c3 | ||
![]() |
31fab3a37a | ||
![]() |
4f416d3818 | ||
![]() |
ffef9fd25a | ||
![]() |
2a6b83f190 | ||
![]() |
1b12dd39cc | ||
![]() |
9162583bac | ||
![]() |
8075998c09 | ||
![]() |
3b6105b713 | ||
![]() |
2b53a2cb12 | ||
![]() |
96d847b6d1 | ||
![]() |
7fde34f85e | ||
![]() |
401e0e6f41 | ||
![]() |
201c274c4b | ||
![]() |
ecde968a0c | ||
![]() |
4a78ce7cfd | ||
![]() |
fda3d56d87 | ||
![]() |
9c3cfca835 | ||
![]() |
1fb689bad3 | ||
![]() |
238ccec5ee | ||
![]() |
c8ae5a3b11 | ||
![]() |
dbe2ce2464 | ||
![]() |
5287183667 | ||
![]() |
a48ae50e1a | ||
![]() |
ca3d55ee4d | ||
![]() |
c0bad42968 | ||
![]() |
7186f1322e | ||
![]() |
21e14b5f9a | ||
![]() |
7d84014ad2 | ||
![]() |
68780da673 | ||
![]() |
1f55a0a358 | ||
![]() |
ba80e16339 | ||
![]() |
11dc14ad4d | ||
![]() |
9f200ebf5a | ||
![]() |
788865e892 | ||
![]() |
e87ee91d8e | ||
![]() |
b0e48d01ce | ||
![]() |
825ee6233b | ||
![]() |
154705e729 | ||
![]() |
636a057373 | ||
![]() |
df1f62477c | ||
![]() |
6907064be6 | ||
![]() |
c1eaf3317d | ||
![]() |
6477a9b056 | ||
![]() |
84f75fe683 | ||
![]() |
7627097875 | ||
![]() |
78824390d0 | ||
![]() |
4d936845f3 | ||
![]() |
76fb80eaeb | ||
![]() |
29b5acf27b | ||
![]() |
e560c6813f | ||
![]() |
a57cbe4636 | ||
![]() |
7cf10d110c | ||
![]() |
1888f21a14 | ||
![]() |
63727f99da | ||
![]() |
602bd189f6 | ||
![]() |
b8314e74db | ||
![]() |
a486bb28f6 | ||
![]() |
b1b5a383e0 | ||
![]() |
b0fe5fd56f | ||
![]() |
398d492f85 | ||
![]() |
55edee58fb | ||
![]() |
da3736bd5f | ||
![]() |
4b2ff5e251 | ||
![]() |
46fc76fdf8 | ||
![]() |
ffbb47452d | ||
![]() |
5ed8b13e4a | ||
![]() |
1baafae3f7 | ||
![]() |
2017ce3699 | ||
![]() |
f715acc92a | ||
![]() |
291691ca0e | ||
![]() |
158732eb17 | ||
![]() |
cdbaff8375 | ||
![]() |
c014dbae8c | ||
![]() |
83d942611f | ||
![]() |
f16f07b36f | ||
![]() |
85cf9e405e | ||
![]() |
a1c00ed87f | ||
![]() |
34d5926ebd | ||
![]() |
6fc8d7746f | ||
![]() |
2fb31a9157 | ||
![]() |
a7e34ab0bc | ||
![]() |
6928488aad | ||
![]() |
8514850ad4 | ||
![]() |
231c38aa41 | ||
![]() |
8d538fad0c | ||
![]() |
f5aa88ca6a | ||
![]() |
b9eb18eb7f | ||
![]() |
b130346ede | ||
![]() |
e8bd47f668 | ||
![]() |
6a918b64bf | ||
![]() |
c82edc38a9 | ||
![]() |
622a42e3aa | ||
![]() |
dcdd7404e4 | ||
![]() |
52c181328c | ||
![]() |
2319cd8211 | ||
![]() |
d0a2257472 | ||
![]() |
af2009710a | ||
![]() |
eec406bb36 | ||
![]() |
83ea328be5 | ||
![]() |
f2c847a1b0 | ||
![]() |
5d03296dc2 | ||
![]() |
b4771bc4f8 | ||
![]() |
68192052fd |
23 changed files with 4302 additions and 442 deletions
22
.github/workflows/ci.yml
vendored
22
.github/workflows/ci.yml
vendored
|
@ -771,7 +771,7 @@ jobs:
|
|||
timeout-minutes: 120
|
||||
name: Create a Windows installer
|
||||
runs-on: [self-hosted, Windows, X64]
|
||||
if: false && (startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-bundling'))
|
||||
if: true && (startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-bundling'))
|
||||
needs: [windows_tests]
|
||||
env:
|
||||
AZURE_TENANT_ID: ${{ secrets.AZURE_SIGNING_TENANT_ID }}
|
||||
|
@ -807,16 +807,16 @@ jobs:
|
|||
name: ZedEditorUserSetup-x64-${{ github.event.pull_request.head.sha || github.sha }}.exe
|
||||
path: ${{ env.SETUP_PATH }}
|
||||
|
||||
- name: Upload Artifacts to release
|
||||
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1
|
||||
# Re-enable when we are ready to publish windows preview releases
|
||||
if: ${{ !(contains(github.event.pull_request.labels.*.name, 'run-bundling')) && env.RELEASE_CHANNEL == 'preview' }} # upload only preview
|
||||
with:
|
||||
draft: true
|
||||
prerelease: ${{ env.RELEASE_CHANNEL == 'preview' }}
|
||||
files: ${{ env.SETUP_PATH }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# - name: Upload Artifacts to release
|
||||
# uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1
|
||||
# # Re-enable when we are ready to publish windows preview releases
|
||||
# if: ${{ !(contains(github.event.pull_request.labels.*.name, 'run-bundling')) && env.RELEASE_CHANNEL == 'preview' }} # upload only preview
|
||||
# with:
|
||||
# draft: true
|
||||
# prerelease: ${{ env.RELEASE_CHANNEL == 'preview' }}
|
||||
# files: ${{ env.SETUP_PATH }}
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
auto-release-preview:
|
||||
name: Auto release preview
|
||||
|
|
|
@ -674,8 +674,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",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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,215 @@ 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,
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
let shader_path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap())
|
||||
.join("src/platform/windows/color_text_raster.hlsl");
|
||||
compile_shader_for_module(
|
||||
"emoji_rasterization",
|
||||
&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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -198,7 +198,7 @@ impl RenderOnce for CharacterGrid {
|
|||
"χ", "ψ", "∂", "а", "в", "Ж", "ж", "З", "з", "К", "к", "л", "м", "Н", "н", "Р", "р",
|
||||
"У", "у", "ф", "ч", "ь", "ы", "Э", "э", "Я", "я", "ij", "öẋ", ".,", "⣝⣑", "~", "*",
|
||||
"_", "^", "`", "'", "(", "{", "«", "#", "&", "@", "$", "¢", "%", "|", "?", "¶", "µ",
|
||||
"❮", "<=", "!=", "==", "--", "++", "=>", "->",
|
||||
"❮", "<=", "!=", "==", "--", "++", "=>", "->", "🏀", "🎊", "😍", "❤️", "👍", "👎",
|
||||
];
|
||||
|
||||
let columns = 11;
|
||||
|
|
|
@ -35,6 +35,7 @@ pub(crate) fn swap_rgba_pa_to_bgra(color: &mut [u8]) {
|
|||
|
||||
/// An RGBA color
|
||||
#[derive(PartialEq, Clone, Copy, Default)]
|
||||
#[repr(C)]
|
||||
pub struct Rgba {
|
||||
/// The red component of the color, in the range 0.0 to 1.0
|
||||
pub r: f32,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -1793,6 +1793,7 @@ impl X11ClientState {
|
|||
drop(state);
|
||||
window.refresh(RequestFrameOptions {
|
||||
require_presentation: expose_event_received,
|
||||
force_render: false,
|
||||
});
|
||||
}
|
||||
xcb_connection
|
||||
|
|
|
@ -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::*;
|
||||
|
|
39
crates/gpui/src/platform/windows/color_text_raster.hlsl
Normal file
39
crates/gpui/src/platform/windows/color_text_raster.hlsl
Normal file
|
@ -0,0 +1,39 @@
|
|||
struct RasterVertexOutput {
|
||||
float4 position : SV_Position;
|
||||
float2 texcoord : TEXCOORD0;
|
||||
};
|
||||
|
||||
RasterVertexOutput emoji_rasterization_vertex(uint vertexID : SV_VERTEXID)
|
||||
{
|
||||
RasterVertexOutput output;
|
||||
output.texcoord = float2((vertexID << 1) & 2, vertexID & 2);
|
||||
output.position = float4(output.texcoord * 2.0f - 1.0f, 0.0f, 1.0f);
|
||||
output.position.y = -output.position.y;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
struct PixelInput {
|
||||
float4 position: SV_Position;
|
||||
float2 texcoord : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct Bounds {
|
||||
int2 origin;
|
||||
int2 size;
|
||||
};
|
||||
|
||||
Texture2D<float4> t_layer : register(t0);
|
||||
SamplerState s_layer : register(s0);
|
||||
|
||||
cbuffer GlyphLayerTextureParams : register(b0) {
|
||||
Bounds bounds;
|
||||
float4 run_color;
|
||||
};
|
||||
|
||||
float4 emoji_rasterization_fragment(PixelInput input): SV_Target {
|
||||
float3 sampled = t_layer.Sample(s_layer, input.texcoord.xy).rgb;
|
||||
float alpha = (sampled.r + sampled.g + sampled.b) / 3;
|
||||
|
||||
return float4(run_color.rgb, alpha);
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
use std::{borrow::Cow, sync::Arc};
|
||||
use std::{borrow::Cow, mem::ManuallyDrop, sync::Arc};
|
||||
|
||||
use ::util::ResultExt;
|
||||
use anyhow::Result;
|
||||
|
@ -10,11 +10,8 @@ use windows::{
|
|||
Foundation::*,
|
||||
Globalization::GetUserDefaultLocaleName,
|
||||
Graphics::{
|
||||
Direct2D::{Common::*, *},
|
||||
DirectWrite::*,
|
||||
Dxgi::Common::*,
|
||||
Gdi::LOGFONTW,
|
||||
Imaging::*,
|
||||
Direct3D::D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, Direct3D11::*, DirectWrite::*,
|
||||
Dxgi::Common::*, Gdi::LOGFONTW, Imaging::*,
|
||||
},
|
||||
System::SystemServices::LOCALE_NAME_MAX_LENGTH,
|
||||
UI::WindowsAndMessaging::*,
|
||||
|
@ -40,16 +37,21 @@ struct DirectWriteComponent {
|
|||
locale: String,
|
||||
factory: IDWriteFactory5,
|
||||
bitmap_factory: AgileReference<IWICImagingFactory>,
|
||||
d2d1_factory: ID2D1Factory,
|
||||
in_memory_loader: IDWriteInMemoryFontFileLoader,
|
||||
builder: IDWriteFontSetBuilder1,
|
||||
text_renderer: Arc<TextRendererWrapper>,
|
||||
render_context: GlyphRenderContext,
|
||||
|
||||
render_params: IDWriteRenderingParams3,
|
||||
gpu_state: GPUState,
|
||||
}
|
||||
|
||||
struct GlyphRenderContext {
|
||||
params: IDWriteRenderingParams3,
|
||||
dc_target: ID2D1DeviceContext4,
|
||||
struct GPUState {
|
||||
device: ID3D11Device,
|
||||
device_context: ID3D11DeviceContext,
|
||||
sampler: [Option<ID3D11SamplerState>; 1],
|
||||
blend_state: ID3D11BlendState,
|
||||
vertex_shader: ID3D11VertexShader,
|
||||
pixel_shader: ID3D11PixelShader,
|
||||
}
|
||||
|
||||
struct DirectWriteState {
|
||||
|
@ -70,12 +72,11 @@ struct FontIdentifier {
|
|||
}
|
||||
|
||||
impl DirectWriteComponent {
|
||||
pub fn new(bitmap_factory: &IWICImagingFactory) -> Result<Self> {
|
||||
pub fn new(bitmap_factory: &IWICImagingFactory, gpu_context: &DirectXDevices) -> Result<Self> {
|
||||
// todo: ideally this would not be a large unsafe block but smaller isolated ones for easier auditing
|
||||
unsafe {
|
||||
let factory: IDWriteFactory5 = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED)?;
|
||||
let bitmap_factory = AgileReference::new(bitmap_factory)?;
|
||||
let d2d1_factory: ID2D1Factory =
|
||||
D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, None)?;
|
||||
// The `IDWriteInMemoryFontFileLoader` here is supported starting from
|
||||
// Windows 10 Creators Update, which consequently requires the entire
|
||||
// `DirectWriteTextSystem` to run on `win10 1703`+.
|
||||
|
@ -86,60 +87,133 @@ impl DirectWriteComponent {
|
|||
GetUserDefaultLocaleName(&mut locale_vec);
|
||||
let locale = String::from_utf16_lossy(&locale_vec);
|
||||
let text_renderer = Arc::new(TextRendererWrapper::new(&locale));
|
||||
let render_context = GlyphRenderContext::new(&factory, &d2d1_factory)?;
|
||||
|
||||
let render_params = {
|
||||
let default_params: IDWriteRenderingParams3 =
|
||||
factory.CreateRenderingParams()?.cast()?;
|
||||
let gamma = default_params.GetGamma();
|
||||
let enhanced_contrast = default_params.GetEnhancedContrast();
|
||||
let gray_contrast = default_params.GetGrayscaleEnhancedContrast();
|
||||
let cleartype_level = default_params.GetClearTypeLevel();
|
||||
let grid_fit_mode = default_params.GetGridFitMode();
|
||||
|
||||
factory.CreateCustomRenderingParams(
|
||||
gamma,
|
||||
enhanced_contrast,
|
||||
gray_contrast,
|
||||
cleartype_level,
|
||||
DWRITE_PIXEL_GEOMETRY_RGB,
|
||||
DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC,
|
||||
grid_fit_mode,
|
||||
)?
|
||||
};
|
||||
|
||||
let gpu_state = GPUState::new(gpu_context)?;
|
||||
|
||||
Ok(DirectWriteComponent {
|
||||
locale,
|
||||
factory,
|
||||
bitmap_factory,
|
||||
d2d1_factory,
|
||||
in_memory_loader,
|
||||
builder,
|
||||
text_renderer,
|
||||
render_context,
|
||||
render_params,
|
||||
gpu_state,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GlyphRenderContext {
|
||||
pub fn new(factory: &IDWriteFactory5, d2d1_factory: &ID2D1Factory) -> Result<Self> {
|
||||
unsafe {
|
||||
let default_params: IDWriteRenderingParams3 =
|
||||
factory.CreateRenderingParams()?.cast()?;
|
||||
let gamma = default_params.GetGamma();
|
||||
let enhanced_contrast = default_params.GetEnhancedContrast();
|
||||
let gray_contrast = default_params.GetGrayscaleEnhancedContrast();
|
||||
let cleartype_level = default_params.GetClearTypeLevel();
|
||||
let grid_fit_mode = default_params.GetGridFitMode();
|
||||
impl GPUState {
|
||||
fn new(gpu_context: &DirectXDevices) -> Result<Self> {
|
||||
// todo: safety comments
|
||||
let device = gpu_context.device.clone();
|
||||
let device_context = gpu_context.device_context.clone();
|
||||
|
||||
let params = factory.CreateCustomRenderingParams(
|
||||
gamma,
|
||||
enhanced_contrast,
|
||||
gray_contrast,
|
||||
cleartype_level,
|
||||
DWRITE_PIXEL_GEOMETRY_RGB,
|
||||
DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC,
|
||||
grid_fit_mode,
|
||||
)?;
|
||||
let dc_target = {
|
||||
let target = d2d1_factory.CreateDCRenderTarget(&get_render_target_property(
|
||||
DXGI_FORMAT_B8G8R8A8_UNORM,
|
||||
D2D1_ALPHA_MODE_PREMULTIPLIED,
|
||||
))?;
|
||||
let target = target.cast::<ID2D1DeviceContext4>()?;
|
||||
target.SetTextRenderingParams(¶ms);
|
||||
target
|
||||
let blend_state = {
|
||||
let mut blend_state = None;
|
||||
let desc = D3D11_BLEND_DESC {
|
||||
AlphaToCoverageEnable: false.into(),
|
||||
IndependentBlendEnable: false.into(),
|
||||
RenderTarget: [
|
||||
D3D11_RENDER_TARGET_BLEND_DESC {
|
||||
BlendEnable: true.into(),
|
||||
SrcBlend: D3D11_BLEND_SRC_ALPHA,
|
||||
DestBlend: D3D11_BLEND_INV_SRC_ALPHA,
|
||||
BlendOp: D3D11_BLEND_OP_ADD,
|
||||
SrcBlendAlpha: D3D11_BLEND_SRC_ALPHA,
|
||||
DestBlendAlpha: D3D11_BLEND_INV_SRC_ALPHA,
|
||||
BlendOpAlpha: D3D11_BLEND_OP_ADD,
|
||||
RenderTargetWriteMask: D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8,
|
||||
},
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
],
|
||||
};
|
||||
unsafe { device.CreateBlendState(&desc, Some(&mut blend_state)) }?;
|
||||
blend_state.unwrap()
|
||||
};
|
||||
|
||||
Ok(Self { params, dc_target })
|
||||
}
|
||||
let sampler = {
|
||||
let mut sampler = None;
|
||||
let desc = D3D11_SAMPLER_DESC {
|
||||
Filter: D3D11_FILTER_MIN_MAG_MIP_POINT,
|
||||
AddressU: D3D11_TEXTURE_ADDRESS_BORDER,
|
||||
AddressV: D3D11_TEXTURE_ADDRESS_BORDER,
|
||||
AddressW: D3D11_TEXTURE_ADDRESS_BORDER,
|
||||
MipLODBias: 0.0,
|
||||
MaxAnisotropy: 1,
|
||||
ComparisonFunc: D3D11_COMPARISON_ALWAYS,
|
||||
BorderColor: [0.0, 0.0, 0.0, 0.0],
|
||||
MinLOD: 0.0,
|
||||
MaxLOD: 0.0,
|
||||
};
|
||||
unsafe { device.CreateSamplerState(&desc, Some(&mut sampler)) }?;
|
||||
[sampler]
|
||||
};
|
||||
|
||||
let vertex_shader = {
|
||||
let source = shader_resources::RawShaderBytes::new(
|
||||
shader_resources::ShaderModule::EmojiRasterization,
|
||||
shader_resources::ShaderTarget::Vertex,
|
||||
)?;
|
||||
let mut shader = None;
|
||||
unsafe { device.CreateVertexShader(source.as_bytes(), None, Some(&mut shader)) }?;
|
||||
shader.unwrap()
|
||||
};
|
||||
|
||||
let pixel_shader = {
|
||||
let source = shader_resources::RawShaderBytes::new(
|
||||
shader_resources::ShaderModule::EmojiRasterization,
|
||||
shader_resources::ShaderTarget::Fragment,
|
||||
)?;
|
||||
let mut shader = None;
|
||||
unsafe { device.CreatePixelShader(source.as_bytes(), None, Some(&mut shader)) }?;
|
||||
shader.unwrap()
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
device,
|
||||
device_context,
|
||||
sampler,
|
||||
blend_state,
|
||||
vertex_shader,
|
||||
pixel_shader,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl DirectWriteTextSystem {
|
||||
pub(crate) fn new(bitmap_factory: &IWICImagingFactory) -> Result<Self> {
|
||||
let components = DirectWriteComponent::new(bitmap_factory)?;
|
||||
pub(crate) fn new(
|
||||
gpu_context: &DirectXDevices,
|
||||
bitmap_factory: &IWICImagingFactory,
|
||||
) -> Result<Self> {
|
||||
let components = DirectWriteComponent::new(bitmap_factory, gpu_context)?;
|
||||
let system_font_collection = unsafe {
|
||||
let mut result = std::mem::zeroed();
|
||||
components
|
||||
|
@ -649,11 +723,6 @@ impl DirectWriteState {
|
|||
}
|
||||
|
||||
fn raster_bounds(&self, params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>> {
|
||||
let render_target = &self.components.render_context.dc_target;
|
||||
unsafe {
|
||||
render_target.SetUnitMode(D2D1_UNIT_MODE_DIPS);
|
||||
render_target.SetDpi(96.0 * params.scale_factor, 96.0 * params.scale_factor);
|
||||
}
|
||||
let font = &self.fonts[params.font_id.0];
|
||||
let glyph_id = [params.glyph_id.0 as u16];
|
||||
let advance = [0.0f32];
|
||||
|
@ -668,25 +737,36 @@ impl DirectWriteState {
|
|||
isSideways: BOOL(0),
|
||||
bidiLevel: 0,
|
||||
};
|
||||
let bounds = unsafe {
|
||||
render_target.GetGlyphRunWorldBounds(
|
||||
Vector2 { X: 0.0, Y: 0.0 },
|
||||
|
||||
let rendering_mode = DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC;
|
||||
let measuring_mode = DWRITE_MEASURING_MODE_NATURAL;
|
||||
let baseline_origin_x = 0.0;
|
||||
let baseline_origin_y = 0.0;
|
||||
|
||||
let transform = DWRITE_MATRIX {
|
||||
m11: params.scale_factor,
|
||||
m12: 0.0,
|
||||
m21: 0.0,
|
||||
m22: params.scale_factor,
|
||||
dx: 0.0,
|
||||
dy: 0.0,
|
||||
};
|
||||
|
||||
let glyph_analysis = unsafe {
|
||||
self.components.factory.CreateGlyphRunAnalysis(
|
||||
&glyph_run,
|
||||
DWRITE_MEASURING_MODE_NATURAL,
|
||||
Some(&transform),
|
||||
rendering_mode,
|
||||
measuring_mode,
|
||||
DWRITE_GRID_FIT_MODE_DEFAULT,
|
||||
DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
|
||||
baseline_origin_x,
|
||||
baseline_origin_y,
|
||||
)?
|
||||
};
|
||||
// todo(windows)
|
||||
// This is a walkaround, deleted when figured out.
|
||||
let y_offset;
|
||||
let extra_height;
|
||||
if params.is_emoji {
|
||||
y_offset = 0;
|
||||
extra_height = 0;
|
||||
} else {
|
||||
// make some room for scaler.
|
||||
y_offset = -1;
|
||||
extra_height = 2;
|
||||
}
|
||||
|
||||
let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1;
|
||||
let bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? };
|
||||
|
||||
if bounds.right < bounds.left {
|
||||
Ok(Bounds {
|
||||
|
@ -695,15 +775,10 @@ impl DirectWriteState {
|
|||
})
|
||||
} else {
|
||||
Ok(Bounds {
|
||||
origin: point(
|
||||
((bounds.left * params.scale_factor).ceil() as i32).into(),
|
||||
((bounds.top * params.scale_factor).ceil() as i32 + y_offset).into(),
|
||||
),
|
||||
origin: point((bounds.left as i32).into(), (bounds.top as i32).into()),
|
||||
size: size(
|
||||
(((bounds.right - bounds.left) * params.scale_factor).ceil() as i32).into(),
|
||||
(((bounds.bottom - bounds.top) * params.scale_factor).ceil() as i32
|
||||
+ extra_height)
|
||||
.into(),
|
||||
(bounds.right - bounds.left).into(),
|
||||
(bounds.bottom - bounds.top).into(),
|
||||
),
|
||||
})
|
||||
}
|
||||
|
@ -739,7 +814,7 @@ impl DirectWriteState {
|
|||
ascenderOffset: glyph_bounds.origin.y.0 as f32 / params.scale_factor,
|
||||
}];
|
||||
let glyph_run = DWRITE_GLYPH_RUN {
|
||||
fontFace: unsafe { std::mem::transmute_copy(&font_info.font_face) },
|
||||
fontFace: ManuallyDrop::new(Some(font_info.font_face.cast()?)),
|
||||
fontEmSize: params.font_size.0,
|
||||
glyphCount: 1,
|
||||
glyphIndices: glyph_id.as_ptr(),
|
||||
|
@ -759,150 +834,398 @@ impl DirectWriteState {
|
|||
}
|
||||
let bitmap_size = bitmap_size;
|
||||
|
||||
let total_bytes;
|
||||
let bitmap_format;
|
||||
let render_target_property;
|
||||
let bitmap_width;
|
||||
let bitmap_height;
|
||||
let bitmap_stride;
|
||||
let bitmap_dpi;
|
||||
if params.is_emoji {
|
||||
total_bytes = bitmap_size.height.0 as usize * bitmap_size.width.0 as usize * 4;
|
||||
bitmap_format = &GUID_WICPixelFormat32bppPBGRA;
|
||||
render_target_property = get_render_target_property(
|
||||
DXGI_FORMAT_B8G8R8A8_UNORM,
|
||||
D2D1_ALPHA_MODE_PREMULTIPLIED,
|
||||
);
|
||||
bitmap_width = bitmap_size.width.0 as u32;
|
||||
bitmap_height = bitmap_size.height.0 as u32;
|
||||
bitmap_stride = bitmap_size.width.0 as u32 * 4;
|
||||
bitmap_dpi = 96.0;
|
||||
let subpixel_shift = params
|
||||
.subpixel_variant
|
||||
.map(|v| v as f32 / SUBPIXEL_VARIANTS as f32);
|
||||
let baseline_origin_x = subpixel_shift.x / params.scale_factor;
|
||||
let baseline_origin_y = subpixel_shift.y / params.scale_factor;
|
||||
|
||||
let transform = DWRITE_MATRIX {
|
||||
m11: params.scale_factor,
|
||||
m12: 0.0,
|
||||
m21: 0.0,
|
||||
m22: params.scale_factor,
|
||||
dx: 0.0,
|
||||
dy: 0.0,
|
||||
};
|
||||
|
||||
let rendering_mode = if params.is_emoji {
|
||||
DWRITE_RENDERING_MODE1_NATURAL
|
||||
} else {
|
||||
total_bytes = bitmap_size.height.0 as usize * bitmap_size.width.0 as usize;
|
||||
bitmap_format = &GUID_WICPixelFormat8bppAlpha;
|
||||
render_target_property =
|
||||
get_render_target_property(DXGI_FORMAT_A8_UNORM, D2D1_ALPHA_MODE_STRAIGHT);
|
||||
bitmap_width = bitmap_size.width.0 as u32 * 2;
|
||||
bitmap_height = bitmap_size.height.0 as u32 * 2;
|
||||
bitmap_stride = bitmap_size.width.0 as u32;
|
||||
bitmap_dpi = 192.0;
|
||||
DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC
|
||||
};
|
||||
|
||||
let measuring_mode = DWRITE_MEASURING_MODE_NATURAL;
|
||||
|
||||
let glyph_analysis = unsafe {
|
||||
self.components.factory.CreateGlyphRunAnalysis(
|
||||
&glyph_run,
|
||||
Some(&transform),
|
||||
rendering_mode,
|
||||
measuring_mode,
|
||||
DWRITE_GRID_FIT_MODE_DEFAULT,
|
||||
DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
|
||||
baseline_origin_x,
|
||||
baseline_origin_y,
|
||||
)?
|
||||
};
|
||||
|
||||
let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1;
|
||||
let texture_bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? };
|
||||
let texture_width = (texture_bounds.right - texture_bounds.left) as u32;
|
||||
let texture_height = (texture_bounds.bottom - texture_bounds.top) as u32;
|
||||
|
||||
if texture_width == 0 || texture_height == 0 {
|
||||
return Ok((
|
||||
bitmap_size,
|
||||
vec![
|
||||
0u8;
|
||||
bitmap_size.width.0 as usize
|
||||
* bitmap_size.height.0 as usize
|
||||
* if params.is_emoji { 4 } else { 1 }
|
||||
],
|
||||
));
|
||||
}
|
||||
|
||||
let bitmap_factory = self.components.bitmap_factory.resolve()?;
|
||||
unsafe {
|
||||
let bitmap = bitmap_factory.CreateBitmap(
|
||||
bitmap_width,
|
||||
bitmap_height,
|
||||
bitmap_format,
|
||||
WICBitmapCacheOnLoad,
|
||||
)?;
|
||||
let render_target = self
|
||||
.components
|
||||
.d2d1_factory
|
||||
.CreateWicBitmapRenderTarget(&bitmap, &render_target_property)?;
|
||||
let brush = render_target.CreateSolidColorBrush(&BRUSH_COLOR, None)?;
|
||||
let subpixel_shift = params
|
||||
.subpixel_variant
|
||||
.map(|v| v as f32 / SUBPIXEL_VARIANTS as f32);
|
||||
let baseline_origin = Vector2 {
|
||||
X: subpixel_shift.x / params.scale_factor,
|
||||
Y: subpixel_shift.y / params.scale_factor,
|
||||
};
|
||||
|
||||
// This `cast()` action here should never fail since we are running on Win10+, and
|
||||
// ID2D1DeviceContext4 requires Win8+
|
||||
let render_target = render_target.cast::<ID2D1DeviceContext4>().unwrap();
|
||||
render_target.SetUnitMode(D2D1_UNIT_MODE_DIPS);
|
||||
render_target.SetDpi(
|
||||
bitmap_dpi * params.scale_factor,
|
||||
bitmap_dpi * params.scale_factor,
|
||||
);
|
||||
render_target.SetTextRenderingParams(&self.components.render_context.params);
|
||||
render_target.BeginDraw();
|
||||
|
||||
if params.is_emoji {
|
||||
// WARN: only DWRITE_GLYPH_IMAGE_FORMATS_COLR has been tested
|
||||
let enumerator = self.components.factory.TranslateColorGlyphRun(
|
||||
baseline_origin,
|
||||
&glyph_run as _,
|
||||
None,
|
||||
DWRITE_GLYPH_IMAGE_FORMATS_COLR
|
||||
| DWRITE_GLYPH_IMAGE_FORMATS_SVG
|
||||
| DWRITE_GLYPH_IMAGE_FORMATS_PNG
|
||||
| DWRITE_GLYPH_IMAGE_FORMATS_JPEG
|
||||
| DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8,
|
||||
DWRITE_MEASURING_MODE_NATURAL,
|
||||
None,
|
||||
0,
|
||||
let mut bitmap_data: Vec<u8>;
|
||||
if params.is_emoji {
|
||||
if let Ok(color) = self.rasterize_color(
|
||||
&glyph_run,
|
||||
rendering_mode,
|
||||
measuring_mode,
|
||||
&transform,
|
||||
point(baseline_origin_x, baseline_origin_y),
|
||||
bitmap_size,
|
||||
) {
|
||||
bitmap_data = color;
|
||||
} else {
|
||||
let monochrome = Self::rasterize_monochrome(
|
||||
&glyph_analysis,
|
||||
bitmap_size,
|
||||
size(texture_width, texture_height),
|
||||
&texture_bounds,
|
||||
)?;
|
||||
while enumerator.MoveNext().is_ok() {
|
||||
let Ok(color_glyph) = enumerator.GetCurrentRun() else {
|
||||
break;
|
||||
};
|
||||
let color_glyph = &*color_glyph;
|
||||
let brush_color = translate_color(&color_glyph.Base.runColor);
|
||||
brush.SetColor(&brush_color);
|
||||
match color_glyph.glyphImageFormat {
|
||||
DWRITE_GLYPH_IMAGE_FORMATS_PNG
|
||||
| DWRITE_GLYPH_IMAGE_FORMATS_JPEG
|
||||
| DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8 => render_target
|
||||
.DrawColorBitmapGlyphRun(
|
||||
color_glyph.glyphImageFormat,
|
||||
baseline_origin,
|
||||
&color_glyph.Base.glyphRun,
|
||||
color_glyph.measuringMode,
|
||||
D2D1_COLOR_BITMAP_GLYPH_SNAP_OPTION_DEFAULT,
|
||||
),
|
||||
DWRITE_GLYPH_IMAGE_FORMATS_SVG => render_target.DrawSvgGlyphRun(
|
||||
baseline_origin,
|
||||
&color_glyph.Base.glyphRun,
|
||||
&brush,
|
||||
None,
|
||||
color_glyph.Base.paletteIndex as u32,
|
||||
color_glyph.measuringMode,
|
||||
),
|
||||
_ => render_target.DrawGlyphRun(
|
||||
baseline_origin,
|
||||
&color_glyph.Base.glyphRun,
|
||||
Some(color_glyph.Base.glyphRunDescription as *const _),
|
||||
&brush,
|
||||
color_glyph.measuringMode,
|
||||
),
|
||||
bitmap_data = monochrome
|
||||
.into_iter()
|
||||
.flat_map(|pixel| [0, 0, 0, pixel])
|
||||
.collect::<Vec<_>>();
|
||||
}
|
||||
} else {
|
||||
bitmap_data = Self::rasterize_monochrome(
|
||||
&glyph_analysis,
|
||||
bitmap_size,
|
||||
size(texture_width, texture_height),
|
||||
&texture_bounds,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok((bitmap_size, bitmap_data))
|
||||
}
|
||||
|
||||
fn rasterize_monochrome(
|
||||
glyph_analysis: &IDWriteGlyphRunAnalysis,
|
||||
bitmap_size: Size<DevicePixels>,
|
||||
texture_size: Size<u32>,
|
||||
texture_bounds: &RECT,
|
||||
) -> Result<Vec<u8>> {
|
||||
let mut bitmap_data =
|
||||
vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize];
|
||||
|
||||
let mut alpha_data = vec![0u8; (texture_size.width * texture_size.height * 3) as usize];
|
||||
|
||||
unsafe {
|
||||
glyph_analysis.CreateAlphaTexture(
|
||||
DWRITE_TEXTURE_CLEARTYPE_3x1,
|
||||
texture_bounds,
|
||||
&mut alpha_data,
|
||||
)?;
|
||||
}
|
||||
|
||||
// Convert ClearType RGB data to grayscale and place in bitmap
|
||||
let offset_x = texture_bounds.left.max(0) as usize;
|
||||
let offset_y = texture_bounds.top.max(0) as usize;
|
||||
|
||||
for y in 0..texture_size.height as usize {
|
||||
for x in 0..texture_size.width as usize {
|
||||
let bitmap_x = offset_x + x;
|
||||
let bitmap_y = offset_y + y;
|
||||
|
||||
if bitmap_x < bitmap_size.width.0 as usize
|
||||
&& bitmap_y < bitmap_size.height.0 as usize
|
||||
{
|
||||
let texture_idx = (y * texture_size.width as usize + x) * 3;
|
||||
let bitmap_idx = bitmap_y * bitmap_size.width.0 as usize + bitmap_x;
|
||||
|
||||
if texture_idx + 2 < alpha_data.len() && bitmap_idx < bitmap_data.len() {
|
||||
let max_value = alpha_data[texture_idx]
|
||||
.max(alpha_data[texture_idx + 1])
|
||||
.max(alpha_data[texture_idx + 2]);
|
||||
bitmap_data[bitmap_idx] = max_value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
render_target.DrawGlyphRun(
|
||||
baseline_origin,
|
||||
&glyph_run,
|
||||
None,
|
||||
&brush,
|
||||
DWRITE_MEASURING_MODE_NATURAL,
|
||||
);
|
||||
}
|
||||
render_target.EndDraw(None, None)?;
|
||||
|
||||
let mut raw_data = vec![0u8; total_bytes];
|
||||
if params.is_emoji {
|
||||
bitmap.CopyPixels(std::ptr::null() as _, bitmap_stride, &mut raw_data)?;
|
||||
// Convert from BGRA with premultiplied alpha to BGRA with straight alpha.
|
||||
for pixel in raw_data.chunks_exact_mut(4) {
|
||||
let a = pixel[3] as f32 / 255.;
|
||||
pixel[0] = (pixel[0] as f32 / a) as u8;
|
||||
pixel[1] = (pixel[1] as f32 / a) as u8;
|
||||
pixel[2] = (pixel[2] as f32 / a) as u8;
|
||||
}
|
||||
} else {
|
||||
let scaler = bitmap_factory.CreateBitmapScaler()?;
|
||||
scaler.Initialize(
|
||||
&bitmap,
|
||||
bitmap_size.width.0 as u32,
|
||||
bitmap_size.height.0 as u32,
|
||||
WICBitmapInterpolationModeHighQualityCubic,
|
||||
)?;
|
||||
scaler.CopyPixels(std::ptr::null() as _, bitmap_stride, &mut raw_data)?;
|
||||
}
|
||||
Ok((bitmap_size, raw_data))
|
||||
}
|
||||
|
||||
Ok(bitmap_data)
|
||||
}
|
||||
|
||||
fn rasterize_color(
|
||||
&self,
|
||||
glyph_run: &DWRITE_GLYPH_RUN,
|
||||
rendering_mode: DWRITE_RENDERING_MODE1,
|
||||
measuring_mode: DWRITE_MEASURING_MODE,
|
||||
transform: &DWRITE_MATRIX,
|
||||
baseline_origin: Point<f32>,
|
||||
bitmap_size: Size<DevicePixels>,
|
||||
) -> Result<Vec<u8>> {
|
||||
// todo: support formats other than COLR
|
||||
let color_enumerator = unsafe {
|
||||
self.components.factory.TranslateColorGlyphRun(
|
||||
Vector2::new(baseline_origin.x, baseline_origin.y),
|
||||
glyph_run,
|
||||
None,
|
||||
DWRITE_GLYPH_IMAGE_FORMATS_COLR,
|
||||
measuring_mode,
|
||||
Some(transform),
|
||||
0,
|
||||
)
|
||||
}?;
|
||||
|
||||
let mut glyph_layers = Vec::new();
|
||||
loop {
|
||||
let color_run = unsafe { color_enumerator.GetCurrentRun() }?;
|
||||
let color_run = unsafe { &*color_run };
|
||||
let image_format = color_run.glyphImageFormat & !DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE;
|
||||
if image_format == DWRITE_GLYPH_IMAGE_FORMATS_COLR {
|
||||
let color_analysis = unsafe {
|
||||
self.components.factory.CreateGlyphRunAnalysis(
|
||||
&color_run.Base.glyphRun as *const _,
|
||||
Some(transform),
|
||||
rendering_mode,
|
||||
measuring_mode,
|
||||
DWRITE_GRID_FIT_MODE_DEFAULT,
|
||||
DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
|
||||
baseline_origin.x,
|
||||
baseline_origin.y,
|
||||
)
|
||||
}?;
|
||||
|
||||
let color_bounds =
|
||||
unsafe { color_analysis.GetAlphaTextureBounds(DWRITE_TEXTURE_CLEARTYPE_3x1) }?;
|
||||
|
||||
let color_size = size(
|
||||
color_bounds.right - color_bounds.left,
|
||||
color_bounds.bottom - color_bounds.top,
|
||||
);
|
||||
if color_size.width > 0 && color_size.height > 0 {
|
||||
let mut alpha_data =
|
||||
vec![0u8; (color_size.width * color_size.height * 3) as usize];
|
||||
unsafe {
|
||||
color_analysis.CreateAlphaTexture(
|
||||
DWRITE_TEXTURE_CLEARTYPE_3x1,
|
||||
&color_bounds,
|
||||
&mut alpha_data,
|
||||
)
|
||||
}?;
|
||||
|
||||
let run_color = {
|
||||
let run_color = color_run.Base.runColor;
|
||||
Rgba {
|
||||
r: run_color.r,
|
||||
g: run_color.g,
|
||||
b: run_color.b,
|
||||
a: run_color.a,
|
||||
}
|
||||
};
|
||||
let bounds = bounds(point(color_bounds.left, color_bounds.top), color_size);
|
||||
let alpha_data = alpha_data
|
||||
.chunks_exact(3)
|
||||
.flat_map(|chunk| [chunk[0], chunk[1], chunk[2], 255])
|
||||
.collect::<Vec<_>>();
|
||||
glyph_layers.push(GlyphLayerTexture::new(
|
||||
&self.components.gpu_state,
|
||||
run_color,
|
||||
bounds,
|
||||
&alpha_data,
|
||||
)?);
|
||||
}
|
||||
}
|
||||
|
||||
let has_next = unsafe { color_enumerator.MoveNext() }
|
||||
.map(|e| e.as_bool())
|
||||
.unwrap_or(false);
|
||||
if !has_next {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let gpu_state = &self.components.gpu_state;
|
||||
let params_buffer = {
|
||||
let desc = D3D11_BUFFER_DESC {
|
||||
ByteWidth: std::mem::size_of::<GlyphLayerTextureParams>() as u32,
|
||||
Usage: D3D11_USAGE_DYNAMIC,
|
||||
BindFlags: D3D11_BIND_CONSTANT_BUFFER.0 as u32,
|
||||
CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
|
||||
MiscFlags: 0,
|
||||
StructureByteStride: 0,
|
||||
};
|
||||
|
||||
let mut buffer = None;
|
||||
unsafe {
|
||||
gpu_state
|
||||
.device
|
||||
.CreateBuffer(&desc, None, Some(&mut buffer))
|
||||
}?;
|
||||
[buffer]
|
||||
};
|
||||
|
||||
let render_target_texture = {
|
||||
let mut texture = None;
|
||||
let desc = D3D11_TEXTURE2D_DESC {
|
||||
Width: bitmap_size.width.0 as u32,
|
||||
Height: bitmap_size.height.0 as u32,
|
||||
MipLevels: 1,
|
||||
ArraySize: 1,
|
||||
Format: DXGI_FORMAT_B8G8R8A8_UNORM,
|
||||
SampleDesc: DXGI_SAMPLE_DESC {
|
||||
Count: 1,
|
||||
Quality: 0,
|
||||
},
|
||||
Usage: D3D11_USAGE_DEFAULT,
|
||||
BindFlags: D3D11_BIND_RENDER_TARGET.0 as u32,
|
||||
CPUAccessFlags: 0,
|
||||
MiscFlags: 0,
|
||||
};
|
||||
unsafe {
|
||||
gpu_state
|
||||
.device
|
||||
.CreateTexture2D(&desc, None, Some(&mut texture))
|
||||
}?;
|
||||
texture.unwrap()
|
||||
};
|
||||
|
||||
let render_target_view = {
|
||||
let desc = D3D11_RENDER_TARGET_VIEW_DESC {
|
||||
Format: DXGI_FORMAT_B8G8R8A8_UNORM,
|
||||
ViewDimension: D3D11_RTV_DIMENSION_TEXTURE2D,
|
||||
Anonymous: D3D11_RENDER_TARGET_VIEW_DESC_0 {
|
||||
Texture2D: D3D11_TEX2D_RTV { MipSlice: 0 },
|
||||
},
|
||||
};
|
||||
let mut rtv = None;
|
||||
unsafe {
|
||||
gpu_state.device.CreateRenderTargetView(
|
||||
&render_target_texture,
|
||||
Some(&desc),
|
||||
Some(&mut rtv),
|
||||
)
|
||||
}?;
|
||||
[rtv]
|
||||
};
|
||||
|
||||
let staging_texture = {
|
||||
let mut texture = None;
|
||||
let desc = D3D11_TEXTURE2D_DESC {
|
||||
Width: bitmap_size.width.0 as u32,
|
||||
Height: bitmap_size.height.0 as u32,
|
||||
MipLevels: 1,
|
||||
ArraySize: 1,
|
||||
Format: DXGI_FORMAT_B8G8R8A8_UNORM,
|
||||
SampleDesc: DXGI_SAMPLE_DESC {
|
||||
Count: 1,
|
||||
Quality: 0,
|
||||
},
|
||||
Usage: D3D11_USAGE_STAGING,
|
||||
BindFlags: 0,
|
||||
CPUAccessFlags: D3D11_CPU_ACCESS_READ.0 as u32,
|
||||
MiscFlags: 0,
|
||||
};
|
||||
unsafe {
|
||||
gpu_state
|
||||
.device
|
||||
.CreateTexture2D(&desc, None, Some(&mut texture))
|
||||
}?;
|
||||
texture.unwrap()
|
||||
};
|
||||
|
||||
let device_context = &gpu_state.device_context;
|
||||
unsafe { device_context.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP) };
|
||||
unsafe { device_context.VSSetShader(&gpu_state.vertex_shader, None) };
|
||||
unsafe { device_context.PSSetShader(&gpu_state.pixel_shader, None) };
|
||||
unsafe { device_context.VSSetConstantBuffers(0, Some(¶ms_buffer)) };
|
||||
unsafe { device_context.PSSetConstantBuffers(0, Some(¶ms_buffer)) };
|
||||
unsafe { device_context.OMSetRenderTargets(Some(&render_target_view), None) };
|
||||
unsafe { device_context.PSSetSamplers(0, Some(&gpu_state.sampler)) };
|
||||
unsafe { device_context.OMSetBlendState(&gpu_state.blend_state, None, 0xffffffff) };
|
||||
|
||||
for layer in glyph_layers {
|
||||
let params = GlyphLayerTextureParams {
|
||||
run_color: layer.run_color,
|
||||
bounds: layer.bounds,
|
||||
};
|
||||
unsafe {
|
||||
let mut dest = std::mem::zeroed();
|
||||
gpu_state.device_context.Map(
|
||||
params_buffer[0].as_ref().unwrap(),
|
||||
0,
|
||||
D3D11_MAP_WRITE_DISCARD,
|
||||
0,
|
||||
Some(&mut dest),
|
||||
)?;
|
||||
std::ptr::copy_nonoverlapping(¶ms as *const _, dest.pData as *mut _, 1);
|
||||
gpu_state
|
||||
.device_context
|
||||
.Unmap(params_buffer[0].as_ref().unwrap(), 0);
|
||||
};
|
||||
|
||||
let texture = [Some(layer.texture_view)];
|
||||
unsafe { device_context.PSSetShaderResources(0, Some(&texture)) };
|
||||
|
||||
let viewport = [D3D11_VIEWPORT {
|
||||
TopLeftX: layer.bounds.origin.x as f32,
|
||||
TopLeftY: layer.bounds.origin.y as f32,
|
||||
Width: layer.bounds.size.width as f32,
|
||||
Height: layer.bounds.size.height as f32,
|
||||
MinDepth: 0.0,
|
||||
MaxDepth: 1.0,
|
||||
}];
|
||||
unsafe { device_context.RSSetViewports(Some(&viewport)) };
|
||||
|
||||
unsafe { device_context.Draw(4, 0) };
|
||||
}
|
||||
|
||||
unsafe { device_context.CopyResource(&staging_texture, &render_target_texture) };
|
||||
|
||||
let mapped_data = {
|
||||
let mut mapped_data = D3D11_MAPPED_SUBRESOURCE::default();
|
||||
unsafe {
|
||||
device_context.Map(
|
||||
&staging_texture,
|
||||
0,
|
||||
D3D11_MAP_READ,
|
||||
0,
|
||||
Some(&mut mapped_data),
|
||||
)
|
||||
}?;
|
||||
mapped_data
|
||||
};
|
||||
let mut rasterized =
|
||||
vec![0u8; (bitmap_size.width.0 as u32 * bitmap_size.height.0 as u32 * 4) as usize];
|
||||
|
||||
for y in 0..bitmap_size.height.0 as usize {
|
||||
let width = bitmap_size.width.0 as usize;
|
||||
unsafe {
|
||||
std::ptr::copy_nonoverlapping::<u8>(
|
||||
(mapped_data.pData as *const u8).byte_add(mapped_data.RowPitch as usize * y),
|
||||
rasterized
|
||||
.as_mut_ptr()
|
||||
.byte_add(width * y * std::mem::size_of::<u32>()),
|
||||
width * std::mem::size_of::<u32>(),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
Ok(rasterized)
|
||||
}
|
||||
|
||||
fn get_typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Bounds<f32>> {
|
||||
|
@ -976,6 +1299,84 @@ impl Drop for DirectWriteState {
|
|||
}
|
||||
}
|
||||
|
||||
struct GlyphLayerTexture {
|
||||
run_color: Rgba,
|
||||
bounds: Bounds<i32>,
|
||||
texture: ID3D11Texture2D,
|
||||
texture_view: ID3D11ShaderResourceView,
|
||||
}
|
||||
|
||||
impl GlyphLayerTexture {
|
||||
pub fn new(
|
||||
gpu_state: &GPUState,
|
||||
run_color: Rgba,
|
||||
bounds: Bounds<i32>,
|
||||
alpha_data: &[u8],
|
||||
) -> Result<Self> {
|
||||
// todo: ideally we would have a nice texture wrapper
|
||||
let texture_size = bounds.size;
|
||||
|
||||
let desc = D3D11_TEXTURE2D_DESC {
|
||||
Width: texture_size.width as u32,
|
||||
Height: texture_size.height as u32,
|
||||
MipLevels: 1,
|
||||
ArraySize: 1,
|
||||
Format: DXGI_FORMAT_R8G8B8A8_UNORM,
|
||||
SampleDesc: DXGI_SAMPLE_DESC {
|
||||
Count: 1,
|
||||
Quality: 0,
|
||||
},
|
||||
Usage: D3D11_USAGE_DEFAULT,
|
||||
BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
|
||||
CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
|
||||
MiscFlags: 0,
|
||||
};
|
||||
|
||||
let texture = {
|
||||
let mut texture: Option<ID3D11Texture2D> = None;
|
||||
unsafe {
|
||||
gpu_state
|
||||
.device
|
||||
.CreateTexture2D(&desc, None, Some(&mut texture))?
|
||||
};
|
||||
texture.unwrap()
|
||||
};
|
||||
let texture_view = {
|
||||
let mut view: Option<ID3D11ShaderResourceView> = None;
|
||||
unsafe {
|
||||
gpu_state
|
||||
.device
|
||||
.CreateShaderResourceView(&texture, None, Some(&mut view))?
|
||||
};
|
||||
view.unwrap()
|
||||
};
|
||||
|
||||
unsafe {
|
||||
gpu_state.device_context.UpdateSubresource(
|
||||
&texture,
|
||||
0,
|
||||
None,
|
||||
alpha_data.as_ptr() as _,
|
||||
(texture_size.width * 4) as u32,
|
||||
0,
|
||||
)
|
||||
};
|
||||
|
||||
Ok(GlyphLayerTexture {
|
||||
run_color,
|
||||
bounds,
|
||||
texture,
|
||||
texture_view,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct GlyphLayerTextureParams {
|
||||
bounds: Bounds<i32>,
|
||||
run_color: Rgba,
|
||||
}
|
||||
|
||||
struct TextRendererWrapper(pub IDWriteTextRenderer);
|
||||
|
||||
impl TextRendererWrapper {
|
||||
|
@ -1470,16 +1871,6 @@ fn get_name(string: IDWriteLocalizedStrings, locale: &str) -> Result<String> {
|
|||
Ok(String::from_utf16_lossy(&name_vec[..name_length]))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn translate_color(color: &DWRITE_COLOR_F) -> D2D1_COLOR_F {
|
||||
D2D1_COLOR_F {
|
||||
r: color.r,
|
||||
g: color.g,
|
||||
b: color.b,
|
||||
a: color.a,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_system_ui_font_name() -> SharedString {
|
||||
unsafe {
|
||||
let mut info: LOGFONTW = std::mem::zeroed();
|
||||
|
@ -1504,24 +1895,6 @@ fn get_system_ui_font_name() -> SharedString {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_render_target_property(
|
||||
pixel_format: DXGI_FORMAT,
|
||||
alpha_mode: D2D1_ALPHA_MODE,
|
||||
) -> D2D1_RENDER_TARGET_PROPERTIES {
|
||||
D2D1_RENDER_TARGET_PROPERTIES {
|
||||
r#type: D2D1_RENDER_TARGET_TYPE_DEFAULT,
|
||||
pixelFormat: D2D1_PIXEL_FORMAT {
|
||||
format: pixel_format,
|
||||
alphaMode: alpha_mode,
|
||||
},
|
||||
dpiX: 96.0,
|
||||
dpiY: 96.0,
|
||||
usage: D2D1_RENDER_TARGET_USAGE_NONE,
|
||||
minLevel: D2D1_FEATURE_LEVEL_DEFAULT,
|
||||
}
|
||||
}
|
||||
|
||||
// One would think that with newer DirectWrite method: IDWriteFontFace4::GetGlyphImageFormats
|
||||
// but that doesn't seem to work for some glyphs, say ❤
|
||||
fn is_color_glyph(
|
||||
|
@ -1561,12 +1934,6 @@ fn is_color_glyph(
|
|||
}
|
||||
|
||||
const DEFAULT_LOCALE_NAME: PCWSTR = windows::core::w!("en-US");
|
||||
const BRUSH_COLOR: D2D1_COLOR_F = D2D1_COLOR_F {
|
||||
r: 1.0,
|
||||
g: 1.0,
|
||||
b: 1.0,
|
||||
a: 1.0,
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
|
310
crates/gpui/src/platform/windows/directx_atlas.rs
Normal file
310
crates/gpui/src/platform/windows/directx_atlas.rs
Normal file
|
@ -0,0 +1,310 @@
|
|||
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::*,
|
||||
};
|
||||
|
||||
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_R8_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],
|
||||
) {
|
||||
println!("{:?}", bounds);
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
1794
crates/gpui/src/platform/windows/directx_renderer.rs
Normal file
1794
crates/gpui/src/platform/windows/directx_renderer.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
|
@ -45,6 +44,7 @@ pub(crate) struct WindowsPlatform {
|
|||
drop_target_helper: IDropTargetHelper,
|
||||
validation_number: usize,
|
||||
main_thread_id_win32: u32,
|
||||
disable_direct_composition: bool,
|
||||
}
|
||||
|
||||
pub(crate) struct WindowsPlatformState {
|
||||
|
@ -94,14 +94,18 @@ impl WindowsPlatform {
|
|||
main_thread_id_win32,
|
||||
validation_number,
|
||||
));
|
||||
let disable_direct_composition = std::env::var(DISABLE_DIRECT_COMPOSITION)
|
||||
.is_ok_and(|value| value == "true" || value == "1");
|
||||
let background_executor = BackgroundExecutor::new(dispatcher.clone());
|
||||
let foreground_executor = ForegroundExecutor::new(dispatcher);
|
||||
let directx_devices = DirectXDevices::new(disable_direct_composition)
|
||||
.context("Unable to init directx devices.")?;
|
||||
let bitmap_factory = ManuallyDrop::new(unsafe {
|
||||
CoCreateInstance(&CLSID_WICImagingFactory, None, CLSCTX_INPROC_SERVER)
|
||||
.context("Error creating bitmap factory.")?
|
||||
});
|
||||
let text_system = Arc::new(
|
||||
DirectWriteTextSystem::new(&bitmap_factory)
|
||||
DirectWriteTextSystem::new(&directx_devices, &bitmap_factory)
|
||||
.context("Error creating DirectWriteTextSystem")?,
|
||||
);
|
||||
let drop_target_helper: IDropTargetHelper = unsafe {
|
||||
|
@ -111,18 +115,17 @@ 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,
|
||||
foreground_executor,
|
||||
text_system,
|
||||
disable_direct_composition,
|
||||
windows_version,
|
||||
bitmap_factory,
|
||||
drop_target_helper,
|
||||
|
@ -187,6 +190,7 @@ impl WindowsPlatform {
|
|||
validation_number: self.validation_number,
|
||||
main_receiver: self.main_receiver.clone(),
|
||||
main_thread_id_win32: self.main_thread_id_win32,
|
||||
disable_direct_composition: self.disable_direct_composition,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -343,27 +347,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 +443,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);
|
||||
|
||||
|
@ -739,6 +722,7 @@ pub(crate) struct WindowCreationInfo {
|
|||
pub(crate) validation_number: usize,
|
||||
pub(crate) main_receiver: flume::Receiver<Runnable>,
|
||||
pub(crate) main_thread_id_win32: u32,
|
||||
pub(crate) disable_direct_composition: bool,
|
||||
}
|
||||
|
||||
fn open_target(target: &str) {
|
||||
|
@ -846,16 +830,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 {
|
||||
|
|
1179
crates/gpui/src/platform/windows/shaders.hlsl
Normal file
1179
crates/gpui/src/platform/windows/shaders.hlsl
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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,
|
||||
|
@ -364,6 +360,7 @@ impl WindowsWindow {
|
|||
validation_number,
|
||||
main_receiver,
|
||||
main_thread_id_win32,
|
||||
disable_direct_composition,
|
||||
} = creation_info;
|
||||
let classname = register_wnd_class(icon);
|
||||
let hide_title_bar = params
|
||||
|
@ -379,14 +376,18 @@ 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 (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 +402,6 @@ impl WindowsWindow {
|
|||
handle,
|
||||
hide_title_bar,
|
||||
display,
|
||||
transparent: true,
|
||||
is_movable: params.is_movable,
|
||||
min_size: params.window_min_size,
|
||||
executor,
|
||||
|
@ -410,9 +410,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 +453,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 +477,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 +696,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 +782,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 +794,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 +1294,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;
|
||||
|
|
|
@ -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| {
|
||||
|
|
|
@ -1 +1 @@
|
|||
dev
|
||||
nightly
|
||||
|
|
|
@ -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}"
|
||||
|
|
|
@ -135,11 +135,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 {
|
||||
|
@ -210,7 +221,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 = @{
|
||||
|
@ -267,6 +277,7 @@ BuildZedAndItsFriends
|
|||
MakeAppx
|
||||
SignZedAndItsFriends
|
||||
ZipZedAndItsFriendsDebug
|
||||
DownloadAMDGpuServices
|
||||
CollectFiles
|
||||
BuildInstaller
|
||||
|
||||
|
|
|
@ -564,7 +564,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" }
|
||||
|
@ -588,7 +587,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"] }
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue