@@ -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(feature = "enable-renderdoc")]
+ // #[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")]
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);
-
- 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);
- }
-
- 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)
+ // 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 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)
+ 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() }?;
-
- 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(())
- }
-}
+// #[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,
+// })
+// }
+
+// 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,
@@ -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;
+}
@@ -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>) {