This commit is contained in:
Junkui Zhang 2025-07-13 13:15:24 +08:00
parent c0bad42968
commit ca3d55ee4d
6 changed files with 904 additions and 220 deletions

View file

@ -84,7 +84,7 @@ impl DirectXAtlas {
let textures = match texture_kind {
AtlasTextureKind::Monochrome => &mut lock.monochrome_textures,
AtlasTextureKind::Polychrome => &mut lock.polychrome_textures,
AtlasTextureKind::Path => &mut lock.path_textures,
// AtlasTextureKind::Path => &mut lock.path_textures,
};
for texture in textures {
texture.clear();
@ -131,7 +131,7 @@ impl DirectXAtlasState {
let textures = match texture_kind {
AtlasTextureKind::Monochrome => &mut self.monochrome_textures,
AtlasTextureKind::Polychrome => &mut self.polychrome_textures,
AtlasTextureKind::Path => &mut self.path_textures,
// AtlasTextureKind::Path => &mut self.path_textures,
};
textures
@ -173,12 +173,11 @@ impl DirectXAtlasState {
pixel_format = DXGI_FORMAT_B8G8R8A8_UNORM;
bind_flag = D3D11_BIND_SHADER_RESOURCE;
bytes_per_pixel = 4;
}
AtlasTextureKind::Path => {
pixel_format = DXGI_FORMAT_R16_FLOAT;
bind_flag = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
bytes_per_pixel = 2;
}
} // AtlasTextureKind::Path => {
// pixel_format = DXGI_FORMAT_R16_FLOAT;
// bind_flag = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
// bytes_per_pixel = 2;
// }
}
let texture_desc = D3D11_TEXTURE2D_DESC {
Width: size.width.0 as u32,
@ -206,16 +205,16 @@ impl DirectXAtlasState {
let textures = match kind {
AtlasTextureKind::Monochrome => &mut self.monochrome_textures,
AtlasTextureKind::Polychrome => &mut self.polychrome_textures,
AtlasTextureKind::Path => &mut self.path_textures,
// AtlasTextureKind::Path => &mut self.path_textures,
};
let rtv = match kind {
AtlasTextureKind::Path => unsafe {
let mut view: Option<ID3D11RenderTargetView> = None;
self.device
.CreateRenderTargetView(&texture, None, Some(&mut view))
.unwrap();
[view]
},
// AtlasTextureKind::Path => unsafe {
// let mut view: Option<ID3D11RenderTargetView> = None;
// self.device
// .CreateRenderTargetView(&texture, None, Some(&mut view))
// .unwrap();
// [view]
// },
_ => [None],
};
let view = unsafe {
@ -244,7 +243,7 @@ impl DirectXAtlasState {
let textures = match id.kind {
crate::AtlasTextureKind::Monochrome => &self.monochrome_textures,
crate::AtlasTextureKind::Polychrome => &self.polychrome_textures,
crate::AtlasTextureKind::Path => &self.path_textures,
// crate::AtlasTextureKind::Path => &self.path_textures,
};
&textures[id.index as usize]
}

View file

@ -3,11 +3,11 @@ use std::{collections::HashMap, hash::BuildHasherDefault, sync::Arc};
use ::util::ResultExt;
use anyhow::{Context, Result};
use collections::FxHasher;
#[cfg(not(feature = "enable-renderdoc"))]
use windows::Win32::Graphics::DirectComposition::*;
// #[cfg(not(feature = "enable-renderdoc"))]
// use windows::Win32::Graphics::DirectComposition::*;
use windows::{
Win32::{
Foundation::HWND,
Foundation::{HMODULE, HWND},
Graphics::{
Direct3D::*,
Direct3D11::*,
@ -41,8 +41,8 @@ struct DirectXContext {
swap_chain: IDXGISwapChain1,
back_buffer: [Option<ID3D11RenderTargetView>; 1],
viewport: [D3D11_VIEWPORT; 1],
#[cfg(not(feature = "enable-renderdoc"))]
direct_composition: DirectComposition,
// #[cfg(not(feature = "enable-renderdoc"))]
// direct_composition: DirectComposition,
}
struct DirectXRenderPipelines {
@ -62,12 +62,12 @@ struct DirectXGlobalElements {
blend_state_for_pr: ID3D11BlendState,
}
#[cfg(not(feature = "enable-renderdoc"))]
struct DirectComposition {
comp_device: IDCompositionDevice,
comp_target: IDCompositionTarget,
comp_visual: IDCompositionVisual,
}
// #[cfg(not(feature = "enable-renderdoc"))]
// struct DirectComposition {
// comp_device: IDCompositionDevice,
// comp_target: IDCompositionTarget,
// comp_visual: IDCompositionVisual,
// }
impl DirectXDevices {
pub(crate) fn new() -> Result<Self> {
@ -91,17 +91,17 @@ impl DirectXDevices {
}
impl DirectXRenderer {
pub(crate) fn new(devices: DirectXDevices, hwnd: HWND, transparent: bool) -> Result<Self> {
pub(crate) fn new(devices: &DirectXDevices, hwnd: HWND, transparent: bool) -> Result<Self> {
let atlas = Arc::new(DirectXAtlas::new(
devices.device.clone(),
devices.device_context.clone(),
));
let context = DirectXContext::new(&devices, hwnd, transparent)?;
let context = DirectXContext::new(devices, hwnd, transparent)?;
let globals = DirectXGlobalElements::new(&devices.device)?;
let pipelines = DirectXRenderPipelines::new(&devices.device)?;
Ok(DirectXRenderer {
atlas,
devices,
devices: devices.clone(),
context,
globals,
pipelines,
@ -110,7 +110,7 @@ impl DirectXRenderer {
})
}
pub(crate) fn spirite_atlas(&self) -> Arc<dyn PlatformAtlas> {
pub(crate) fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
self.atlas.clone()
}
@ -153,7 +153,7 @@ impl DirectXRenderer {
scene.polychrome_sprites.len(),
scene.surfaces.len(),))?;
}
unsafe { self.context.swap_chain.Present(0, 0) }.ok()?;
unsafe { self.context.swap_chain.Present(0, DXGI_PRESENT(0)) }.ok()?;
Ok(())
}
@ -166,7 +166,7 @@ impl DirectXRenderer {
new_size.width.0 as u32,
new_size.height.0 as u32,
DXGI_FORMAT_B8G8R8A8_UNORM,
0,
DXGI_SWAP_CHAIN_FLAG(0),
)?;
}
let backbuffer = set_render_target_view(
@ -183,32 +183,32 @@ impl DirectXRenderer {
Ok(())
}
#[cfg(not(feature = "enable-renderdoc"))]
pub(crate) fn update_transparency(
&mut self,
background_appearance: WindowBackgroundAppearance,
) -> Result<()> {
// We only support setting `Transparent` and `Opaque` for now.
match background_appearance {
WindowBackgroundAppearance::Opaque => {
if self.transparent {
return Err(anyhow::anyhow!(
"Set opaque backgroud from transparent background, a restart is required. Or, you can open a new window."
));
}
}
WindowBackgroundAppearance::Transparent | WindowBackgroundAppearance::Blurred => {
if !self.transparent {
return Err(anyhow::anyhow!(
"Set transparent backgroud from opaque background, a restart is required. Or, you can open a new window."
));
}
}
}
Ok(())
}
// #[cfg(not(feature = "enable-renderdoc"))]
// pub(crate) fn update_transparency(
// &mut self,
// background_appearance: WindowBackgroundAppearance,
// ) -> Result<()> {
// // We only support setting `Transparent` and `Opaque` for now.
// match background_appearance {
// WindowBackgroundAppearance::Opaque => {
// if self.transparent {
// return Err(anyhow::anyhow!(
// "Set opaque backgroud from transparent background, a restart is required. Or, you can open a new window."
// ));
// }
// }
// WindowBackgroundAppearance::Transparent | WindowBackgroundAppearance::Blurred => {
// if !self.transparent {
// return Err(anyhow::anyhow!(
// "Set transparent backgroud from opaque background, a restart is required. Or, you can open a new window."
// ));
// }
// }
// }
// Ok(())
// }
#[cfg(feature = "enable-renderdoc")]
// #[cfg(feature = "enable-renderdoc")]
pub(crate) fn update_transparency(
&mut self,
background_appearance: WindowBackgroundAppearance,
@ -280,77 +280,78 @@ impl DirectXRenderer {
&mut self,
paths: &[Path<ScaledPixels>],
) -> Option<HashMap<PathId, AtlasTile>> {
self.atlas.clear_textures(AtlasTextureKind::Path);
// self.atlas.clear_textures(AtlasTextureKind::Path);
let mut tiles = HashMap::default();
let mut vertices_by_texture_id: HashMap<
AtlasTextureId,
Vec<PathVertex<ScaledPixels>>,
BuildHasherDefault<FxHasher>,
> = HashMap::default();
for path in paths {
let clipped_bounds = path.bounds.intersect(&path.content_mask.bounds);
// let mut tiles = HashMap::default();
// let mut vertices_by_texture_id: HashMap<
// AtlasTextureId,
// Vec<PathVertex<ScaledPixels>>,
// BuildHasherDefault<FxHasher>,
// > = HashMap::default();
// for path in paths {
// let clipped_bounds = path.bounds.intersect(&path.content_mask.bounds);
let tile = self
.atlas
.allocate(clipped_bounds.size.map(Into::into), AtlasTextureKind::Path)?;
vertices_by_texture_id
.entry(tile.texture_id)
.or_insert(Vec::new())
.extend(path.vertices.iter().map(|vertex| PathVertex {
xy_position: vertex.xy_position - clipped_bounds.origin
+ tile.bounds.origin.map(Into::into),
content_mask: ContentMask {
bounds: tile.bounds.map(Into::into),
},
}));
tiles.insert(path.id, tile);
}
// let tile = self
// .atlas
// .allocate(clipped_bounds.size.map(Into::into), AtlasTextureKind::Path)?;
// vertices_by_texture_id
// .entry(tile.texture_id)
// .or_insert(Vec::new())
// .extend(path.vertices.iter().map(|vertex| PathVertex {
// xy_position: vertex.xy_position - clipped_bounds.origin
// + tile.bounds.origin.map(Into::into),
// content_mask: ContentMask {
// bounds: tile.bounds.map(Into::into),
// },
// }));
// tiles.insert(path.id, tile);
// }
for (texture_id, vertices) in vertices_by_texture_id {
let (texture_size, rtv) = self.atlas.get_texture_drawing_info(texture_id);
let viewport = [D3D11_VIEWPORT {
TopLeftX: 0.0,
TopLeftY: 0.0,
Width: texture_size.width,
Height: texture_size.height,
MinDepth: 0.0,
MaxDepth: 1.0,
}];
pre_draw(
&self.devices.device_context,
&self.globals.global_params_buffer,
&viewport,
&rtv,
[0.0, 0.0, 0.0, 1.0],
&self.globals.blend_state_for_pr,
)
.log_err()?;
update_buffer_capacity(
&self.pipelines.path_raster_pipeline,
std::mem::size_of::<PathVertex<ScaledPixels>>(),
vertices.len(),
&self.devices.device,
)
.map(|input| update_pipeline(&mut self.pipelines.path_raster_pipeline, input));
update_buffer(
&self.devices.device_context,
&self.pipelines.path_raster_pipeline.buffer,
&vertices,
)
.log_err()?;
draw_normal(
&self.devices.device_context,
&self.pipelines.path_raster_pipeline,
&viewport,
&self.globals.global_params_buffer,
D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
vertices.len() as u32,
1,
)
.log_err()?;
}
Some(tiles)
// for (texture_id, vertices) in vertices_by_texture_id {
// let (texture_size, rtv) = self.atlas.get_texture_drawing_info(texture_id);
// let viewport = [D3D11_VIEWPORT {
// TopLeftX: 0.0,
// TopLeftY: 0.0,
// Width: texture_size.width,
// Height: texture_size.height,
// MinDepth: 0.0,
// MaxDepth: 1.0,
// }];
// pre_draw(
// &self.devices.device_context,
// &self.globals.global_params_buffer,
// &viewport,
// &rtv,
// [0.0, 0.0, 0.0, 1.0],
// &self.globals.blend_state_for_pr,
// )
// .log_err()?;
// update_buffer_capacity(
// &self.pipelines.path_raster_pipeline,
// std::mem::size_of::<PathVertex<ScaledPixels>>(),
// vertices.len(),
// &self.devices.device,
// )
// .map(|input| update_pipeline(&mut self.pipelines.path_raster_pipeline, input));
// update_buffer(
// &self.devices.device_context,
// &self.pipelines.path_raster_pipeline.buffer,
// &vertices,
// )
// .log_err()?;
// draw_normal(
// &self.devices.device_context,
// &self.pipelines.path_raster_pipeline,
// &viewport,
// &self.globals.global_params_buffer,
// D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
// vertices.len() as u32,
// 1,
// )
// .log_err()?;
// }
// Some(tiles)
None
}
fn draw_paths(
@ -358,43 +359,43 @@ impl DirectXRenderer {
paths: &[Path<ScaledPixels>],
path_tiles: &HashMap<PathId, AtlasTile>,
) -> Result<()> {
if paths.is_empty() {
return Ok(());
}
for path in paths {
let tile = &path_tiles[&path.id];
let texture_view = self.atlas.get_texture_view(tile.texture_id);
let origin = path.bounds.intersect(&path.content_mask.bounds).origin;
let sprites = [PathSprite {
bounds: Bounds {
origin: origin.map(|p| p.floor()),
size: tile.bounds.size.map(Into::into),
},
color: path.color,
tile: (*tile).clone(),
}];
update_buffer_capacity(
&self.pipelines.paths_pipeline,
std::mem::size_of::<PathSprite>(),
1,
&self.devices.device,
)
.map(|input| update_pipeline(&mut self.pipelines.paths_pipeline, input));
update_buffer(
&self.devices.device_context,
&self.pipelines.paths_pipeline.buffer,
&sprites,
)?;
draw_with_texture(
&self.devices.device_context,
&self.pipelines.paths_pipeline,
&texture_view,
&self.context.viewport,
&self.globals.global_params_buffer,
&self.globals.sampler,
1,
)?;
}
// if paths.is_empty() {
// return Ok(());
// }
// for path in paths {
// let tile = &path_tiles[&path.id];
// let texture_view = self.atlas.get_texture_view(tile.texture_id);
// let origin = path.bounds.intersect(&path.content_mask.bounds).origin;
// let sprites = [PathSprite {
// bounds: Bounds {
// origin: origin.map(|p| p.floor()),
// size: tile.bounds.size.map(Into::into),
// },
// color: path.color,
// tile: (*tile).clone(),
// }];
// update_buffer_capacity(
// &self.pipelines.paths_pipeline,
// std::mem::size_of::<PathSprite>(),
// 1,
// &self.devices.device,
// )
// .map(|input| update_pipeline(&mut self.pipelines.paths_pipeline, input));
// update_buffer(
// &self.devices.device_context,
// &self.pipelines.paths_pipeline.buffer,
// &sprites,
// )?;
// draw_with_texture(
// &self.devices.device_context,
// &self.pipelines.paths_pipeline,
// &texture_view,
// &self.context.viewport,
// &self.globals.global_params_buffer,
// &self.globals.sampler,
// 1,
// )?;
// }
Ok(())
}
@ -489,7 +490,7 @@ impl DirectXRenderer {
)
}
fn draw_surfaces(&mut self, surfaces: &[Surface]) -> Result<()> {
fn draw_surfaces(&mut self, surfaces: &[PaintSurface]) -> Result<()> {
if surfaces.is_empty() {
return Ok(());
}
@ -499,15 +500,15 @@ impl DirectXRenderer {
impl DirectXContext {
pub fn new(devices: &DirectXDevices, hwnd: HWND, transparent: bool) -> Result<Self> {
#[cfg(not(feature = "enable-renderdoc"))]
let swap_chain = create_swap_chain(&devices.dxgi_factory, &devices.device, transparent)?;
#[cfg(feature = "enable-renderdoc")]
// #[cfg(not(feature = "enable-renderdoc"))]
// let swap_chain = create_swap_chain(&devices.dxgi_factory, &devices.device, transparent)?;
// #[cfg(feature = "enable-renderdoc")]
let swap_chain =
create_swap_chain_default(&devices.dxgi_factory, &devices.device, hwnd, transparent)?;
#[cfg(not(feature = "enable-renderdoc"))]
let direct_composition = DirectComposition::new(&devices.dxgi_device, hwnd)?;
#[cfg(not(feature = "enable-renderdoc"))]
direct_composition.set_swap_chain(&swap_chain)?;
// #[cfg(not(feature = "enable-renderdoc"))]
// let direct_composition = DirectComposition::new(&devices.dxgi_device, hwnd)?;
// #[cfg(not(feature = "enable-renderdoc"))]
// direct_composition.set_swap_chain(&swap_chain)?;
let back_buffer = [Some(set_render_target_view(
&swap_chain,
&devices.device,
@ -520,8 +521,8 @@ impl DirectXContext {
swap_chain,
back_buffer,
viewport,
#[cfg(not(feature = "enable-renderdoc"))]
direct_composition,
// #[cfg(not(feature = "enable-renderdoc"))]
// direct_composition,
})
}
}
@ -590,29 +591,29 @@ impl DirectXRenderPipelines {
}
}
#[cfg(not(feature = "enable-renderdoc"))]
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() }?;
// #[cfg(not(feature = "enable-renderdoc"))]
// 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,
})
}
// 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(())
}
}
// 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> {
@ -726,7 +727,7 @@ fn get_device(
D3D11CreateDevice(
adapter,
D3D_DRIVER_TYPE_UNKNOWN,
None,
HMODULE::default(),
device_flags,
Some(&[D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1]),
D3D11_SDK_VERSION,
@ -737,10 +738,10 @@ fn get_device(
})
}
#[cfg(not(feature = "enable-renderdoc"))]
fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result<IDCompositionDevice> {
Ok(unsafe { DCompositionCreateDevice(dxgi_device)? })
}
// #[cfg(not(feature = "enable-renderdoc"))]
// fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result<IDCompositionDevice> {
// Ok(unsafe { DCompositionCreateDevice(dxgi_device)? })
// }
fn create_swap_chain(
dxgi_factory: &IDXGIFactory6,
@ -772,7 +773,7 @@ fn create_swap_chain(
Ok(unsafe { dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? })
}
#[cfg(feature = "enable-renderdoc")]
// #[cfg(feature = "enable-renderdoc")]
fn create_swap_chain_default(
dxgi_factory: &IDXGIFactory6,
device: &ID3D11Device,

View file

@ -181,11 +181,13 @@ 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.renderer
// .update_drawable_size_even_if_unchanged(new_size);
lock.renderer.resize(new_size).log_err();
lock.callbacks.request_frame = lock.restore_from_minimized.take();
} else {
lock.renderer.update_drawable_size(new_size);
// 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;

View file

@ -34,7 +34,8 @@ 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,
// gpu_context: BladeContext,
directx_devices: DirectXDevices,
icon: HICON,
main_receiver: flume::Receiver<Runnable>,
background_executor: BackgroundExecutor,
@ -111,13 +112,14 @@ 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 gpu_context = BladeContext::new().context("Unable to init GPU context")?;
let directx_devices = DirectXDevices::new().context("Unable to init directx devices.")?;
let windows_version = WindowsVersion::new().context("Error retrieve windows version")?;
Ok(Self {
state,
raw_window_handles,
gpu_context,
directx_devices,
icon,
main_receiver,
background_executor,
@ -459,7 +461,7 @@ impl Platform for WindowsPlatform {
handle,
options,
self.generate_creation_info(),
&self.gpu_context,
&self.directx_devices,
)?;
let handle = window.get_raw_handle();
self.raw_window_handles.write().push(handle);

View file

@ -0,0 +1,676 @@
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 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(float2 unit_vertex, Bounds bounds) {
float2 position = unit_vertex * bounds.size + bounds.origin;
float2 device_position = position / global_viewport_size * float2(2.0, -2.0) + float2(-1.0, 1.0);
return float4(device_position, 0., 1.);
}
float4 distance_from_clip_rect(float2 unit_vertex, Bounds bounds, Bounds clip_bounds) {
float2 position = unit_vertex * bounds.size + bounds.origin;
return float4(position.x - clip_bounds.origin.x,
clip_bounds.origin.x + clip_bounds.size.x - position.x,
position.y - clip_bounds.origin.y,
clip_bounds.origin.y + clip_bounds.size.y - position.y);
}
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;
}
// 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;
}
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);
}
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;
if (center_to_point.x < 0.) {
if (center_to_point.y < 0.) {
corner_radius = corner_radii.top_left;
} else {
corner_radius = corner_radii.bottom_left;
}
} else {
if (center_to_point.y < 0.) {
corner_radius = corner_radii.top_right;
} else {
corner_radius = corner_radii.bottom_right;
}
}
float2 rounded_edge_to_point = abs(center_to_point) - half_size + corner_radius;
float distance =
length(max(0., rounded_edge_to_point)) +
min(0., max(rounded_edge_to_point.x, rounded_edge_to_point.y)) -
corner_radius;
return distance;
}
/*
**
** Shadows
**
*/
struct ShadowVertexOutput {
float4 position: SV_Position;
float4 color: COLOR;
uint shadow_id: FLAT;
float4 clip_distance: SV_ClipDistance;
};
struct ShadowFragmentInput {
float4 position: SV_Position;
float4 color: COLOR;
uint shadow_id: FLAT;
};
struct Shadow {
uint order;
float blur_radius;
Bounds bounds;
Corners corner_radii;
Bounds content_mask;
Hsla 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;
if (point0.x < 0.) {
if (point0.y < 0.) {
corner_radius = shadow.corner_radii.top_left;
} else {
corner_radius = shadow.corner_radii.bottom_left;
}
} else {
if (point0.y < 0.) {
corner_radius = shadow.corner_radii.top_right;
} else {
corner_radius = shadow.corner_radii.bottom_right;
}
}
// 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);
}
/*
**
** Quads
**
*/
struct Quad {
uint order;
uint pad;
Bounds bounds;
Bounds content_mask;
Hsla background;
Hsla border_color;
Corners corner_radii;
Edges border_widths;
};
struct QuadVertexOutput {
float4 position: SV_Position;
float4 background_color: COLOR0;
float4 border_color: COLOR1;
uint quad_id: FLAT;
float4 clip_distance: SV_ClipDistance;
};
struct QuadFragmentInput {
float4 position: SV_Position;
float4 background_color: COLOR0;
float4 border_color: COLOR1;
uint quad_id: FLAT;
};
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);
float4 clip_distance = distance_from_clip_rect(unit_vertex, quad.bounds, quad.content_mask);
float4 background_color = hsla_to_rgba(quad.background);
float4 border_color = hsla_to_rgba(quad.border_color);
QuadVertexOutput output;
output.position = device_position;
output.background_color = background_color;
output.border_color = border_color;
output.quad_id = quad_id;
output.clip_distance = clip_distance;
return output;
}
float4 quad_fragment(QuadFragmentInput input): SV_Target {
Quad quad = quads[input.quad_id];
// Fast path when the quad is not rounded and doesn't have any border.
if (quad.corner_radii.top_left == 0. && quad.corner_radii.bottom_left == 0. &&
quad.corner_radii.top_right == 0. &&
quad.corner_radii.bottom_right == 0. && quad.border_widths.top == 0. &&
quad.border_widths.left == 0. && quad.border_widths.right == 0. &&
quad.border_widths.bottom == 0.) {
return input.background_color;
}
float2 half_size = quad.bounds.size / 2.;
float2 center = quad.bounds.origin + half_size;
float2 center_to_point = input.position.xy - center;
float corner_radius;
if (center_to_point.x < 0.) {
if (center_to_point.y < 0.) {
corner_radius = quad.corner_radii.top_left;
} else {
corner_radius = quad.corner_radii.bottom_left;
}
} else {
if (center_to_point.y < 0.) {
corner_radius = quad.corner_radii.top_right;
} else {
corner_radius = quad.corner_radii.bottom_right;
}
}
float2 rounded_edge_to_point = abs(center_to_point) - half_size + corner_radius;
float distance =
length(max(0., rounded_edge_to_point)) +
min(0., max(rounded_edge_to_point.x, rounded_edge_to_point.y)) -
corner_radius;
float vertical_border = center_to_point.x <= 0. ? quad.border_widths.left
: quad.border_widths.right;
float horizontal_border = center_to_point.y <= 0. ? quad.border_widths.top
: quad.border_widths.bottom;
float2 inset_size = half_size - corner_radius - float2(vertical_border, horizontal_border);
float2 point_to_inset_corner = abs(center_to_point) - inset_size;
float border_width;
if (point_to_inset_corner.x < 0. && point_to_inset_corner.y < 0.) {
border_width = 0.;
} else if (point_to_inset_corner.y > point_to_inset_corner.x) {
border_width = horizontal_border;
} else {
border_width = vertical_border;
}
float4 color;
if (border_width == 0.) {
color = input.background_color;
} else {
float inset_distance = distance + border_width;
// 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(input.background_color, input.border_color);
color = lerp(blended_border, input.background_color,
saturate(0.5 - inset_distance));
}
return color * float4(1., 1., 1., saturate(0.5 - distance));
}
/*
**
** Path raster
**
*/
struct PathVertex {
float2 xy_position;
float2 st_position;
Bounds content_mask;
};
struct PathRasterizationOutput {
float4 position: SV_Position;
float2 st_position: TEXCOORD0;
float4 clip_distances: SV_ClipDistance;
};
struct PathRasterizationInput {
float4 position: SV_Position;
float2 st_position: TEXCOORD0;
};
StructuredBuffer<PathVertex> path_vertices: register(t1);
PathRasterizationOutput path_rasterization_vertex(uint vertex_id: SV_VertexID) {
PathVertex vertex = path_vertices[vertex_id];
PathRasterizationOutput output;
float2 device_position = vertex.xy_position / global_viewport_size * float2(2.0, -2.0) + float2(-1.0, 1.0);
float2 tl = vertex.xy_position - vertex.content_mask.origin;
float2 br = vertex.content_mask.origin + vertex.content_mask.size - vertex.xy_position;
output.position = float4(device_position, 0.0, 1.0);
output.st_position = vertex.st_position;
output.clip_distances = float4(tl.x, br.x, tl.y, br.y);
return output;
}
float4 path_rasterization_fragment(PathRasterizationInput input): SV_Target {
float2 dx = ddx(input.st_position);
float2 dy = ddy(input.st_position);
float2 gradient = float2((2. * input.st_position.x) * dx.x - dx.y,
(2. * input.st_position.x) * dy.x - dy.y);
float f = (input.st_position.x * input.st_position.x) - input.st_position.y;
float distance = f / length(gradient);
float alpha = saturate(0.5 - distance);
return float4(alpha, 0., 0., 1.);
}
/*
**
** Paths
**
*/
struct PathSprite {
Bounds bounds;
Hsla color;
AtlasTile tile;
};
struct PathVertexOutput {
float4 position: SV_Position;
float2 tile_position: POSITION1;
float4 color: COLOR;
};
StructuredBuffer<PathSprite> path_sprites: register(t1);
PathVertexOutput paths_vertex(uint vertex_id: SV_VertexID, uint instance_id: SV_InstanceID) {
float2 unit_vertex = float2(float(vertex_id & 1u), 0.5 * float(vertex_id & 2u));
PathSprite sprite = path_sprites[instance_id];
// Don't apply content mask because it was already accounted for when rasterizing the path.
PathVertexOutput output;
output.position = to_device_position(unit_vertex, sprite.bounds);
output.tile_position = to_tile_position(unit_vertex, sprite.tile);
output.color = hsla_to_rgba(sprite.color);
return output;
}
float4 paths_fragment(PathVertexOutput input): SV_Target {
float sample = t_sprite.Sample(s_sprite, input.tile_position).r;
float mask = 1.0 - abs(1.0 - sample % 2.0);
float4 color = input.color;
color.a *= mask;
return color;
}
/*
**
** Underlines
**
*/
struct Underline {
uint order;
uint pad;
Bounds bounds;
Bounds content_mask;
Hsla color;
float thickness;
uint wavy;
};
struct UnderlineVertexOutput {
float4 position: SV_Position;
float4 color: COLOR;
uint underline_id: FLAT;
float4 clip_distance: SV_ClipDistance;
};
struct UnderlineFragmentInput {
float4 position: SV_Position;
float4 color: COLOR;
uint underline_id: FLAT;
};
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 =
float2(underline.bounds.origin.x, underline.bounds.origin.y);
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;
float4 color: COLOR;
float4 clip_distance: SV_ClipDistance;
};
struct MonochromeSpriteFragmentInput {
float4 position: SV_Position;
float2 tile_position: POSITION;
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 grayscale;
Bounds bounds;
Bounds content_mask;
Corners corner_radii;
AtlasTile tile;
};
struct PolychromeSpriteVertexOutput {
float4 position: SV_Position;
float2 tile_position: POSITION;
uint sprite_id: FLAT;
float4 clip_distance: SV_ClipDistance;
};
struct PolychromeSpriteFragmentInput {
float4 position: SV_Position;
float2 tile_position: POSITION;
uint sprite_id: FLAT;
};
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 *= saturate(0.5 - distance);
return color;
}

View file

@ -49,7 +49,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,
@ -84,7 +84,7 @@ impl WindowsWindowState {
cs: &CREATESTRUCTW,
current_cursor: Option<HCURSOR>,
display: WindowsDisplay,
gpu_context: &BladeContext,
gpu_context: &DirectXDevices,
min_size: Option<Size<Pixels>>,
appearance: WindowAppearance,
) -> Result<Self> {
@ -103,7 +103,8 @@ impl WindowsWindowState {
};
let border_offset = WindowBorderOffset::default();
let restore_from_minimized = None;
let renderer = windows_renderer::init(gpu_context, hwnd, transparent)?;
// let renderer = windows_renderer::init(gpu_context, hwnd, transparent)?;
let renderer = DirectXRenderer::new(gpu_context, hwnd, transparent)?;
let callbacks = Callbacks::default();
let input_handler = None;
let pending_surrogate = None;
@ -343,7 +344,7 @@ struct WindowCreateContext<'a> {
drop_target_helper: IDropTargetHelper,
validation_number: usize,
main_receiver: flume::Receiver<Runnable>,
gpu_context: &'a BladeContext,
gpu_context: &'a DirectXDevices,
main_thread_id_win32: u32,
appearance: WindowAppearance,
}
@ -353,7 +354,7 @@ impl WindowsWindow {
handle: AnyWindowHandle,
params: WindowParams,
creation_info: WindowCreationInfo,
gpu_context: &BladeContext,
gpu_context: &DirectXDevices,
) -> Result<Self> {
let WindowCreationInfo {
icon,
@ -485,7 +486,7 @@ impl rwh::HasDisplayHandle for WindowsWindow {
impl Drop for WindowsWindow {
fn drop(&mut self) {
self.0.state.borrow_mut().renderer.destroy();
// self.0.state.borrow_mut().renderer.destroy();
// clone this `Rc` to prevent early release of the pointer
let this = self.0.clone();
self.0
@ -706,9 +707,10 @@ 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);
// todo(zjk)
// window_state
// .renderer
// .update_transparency(background_appearance != WindowBackgroundAppearance::Opaque);
match background_appearance {
WindowBackgroundAppearance::Opaque => {
@ -794,11 +796,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,7 +808,9 @@ impl PlatformWindow for WindowsWindow {
}
fn gpu_specs(&self) -> Option<GpuSpecs> {
Some(self.0.state.borrow().renderer.gpu_specs())
// todo(zjk)
// Some(self.0.state.borrow().renderer.gpu_specs())
None
}
fn update_ime_position(&self, _bounds: Bounds<ScaledPixels>) {