add transparency

This commit is contained in:
Junkui Zhang 2025-07-17 11:29:44 +08:00
parent b1b5a383e0
commit b8314e74db
3 changed files with 166 additions and 154 deletions

View file

@ -666,6 +666,7 @@ features = [
"Win32_Graphics_Direct3D", "Win32_Graphics_Direct3D",
"Win32_Graphics_Direct3D11", "Win32_Graphics_Direct3D11",
"Win32_Graphics_Direct3D_Fxc", "Win32_Graphics_Direct3D_Fxc",
"Win32_Graphics_DirectComposition",
"Win32_Graphics_DirectWrite", "Win32_Graphics_DirectWrite",
"Win32_Graphics_Dwm", "Win32_Graphics_Dwm",
"Win32_Graphics_Dxgi", "Win32_Graphics_Dxgi",

View file

@ -3,7 +3,8 @@ use std::{mem::ManuallyDrop, sync::Arc};
use ::util::ResultExt; use ::util::ResultExt;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
// #[cfg(not(feature = "enable-renderdoc"))] // #[cfg(not(feature = "enable-renderdoc"))]
// use windows::Win32::Graphics::DirectComposition::*; use windows::Win32::Graphics::DirectComposition::*;
use windows::Win32::UI::WindowsAndMessaging::GetWindowRect;
use windows::{ use windows::{
Win32::{ Win32::{
Foundation::{HMODULE, HWND}, Foundation::{HMODULE, HWND},
@ -26,7 +27,6 @@ pub(crate) struct DirectXRenderer {
context: DirectXContext, context: DirectXContext,
globals: DirectXGlobalElements, globals: DirectXGlobalElements,
pipelines: DirectXRenderPipelines, pipelines: DirectXRenderPipelines,
hwnd: HWND,
transparent: bool, transparent: bool,
} }
@ -46,7 +46,7 @@ struct DirectXContext {
msaa_view: ID3D11RenderTargetView, msaa_view: ID3D11RenderTargetView,
viewport: [D3D11_VIEWPORT; 1], viewport: [D3D11_VIEWPORT; 1],
// #[cfg(not(feature = "enable-renderdoc"))] // #[cfg(not(feature = "enable-renderdoc"))]
// direct_composition: DirectComposition, direct_composition: DirectComposition,
} }
struct DirectXRenderPipelines { struct DirectXRenderPipelines {
@ -73,11 +73,11 @@ struct DrawInstancedIndirectArgs {
} }
// #[cfg(not(feature = "enable-renderdoc"))] // #[cfg(not(feature = "enable-renderdoc"))]
// struct DirectComposition { struct DirectComposition {
// comp_device: IDCompositionDevice, comp_device: IDCompositionDevice,
// comp_target: IDCompositionTarget, comp_target: IDCompositionTarget,
// comp_visual: IDCompositionVisual, comp_visual: IDCompositionVisual,
// } }
impl DirectXDevices { impl DirectXDevices {
pub(crate) fn new() -> Result<Self> { pub(crate) fn new() -> Result<Self> {
@ -115,7 +115,6 @@ impl DirectXRenderer {
context, context,
globals, globals,
pipelines, pipelines,
hwnd,
transparent, transparent,
}) })
} }
@ -215,11 +214,6 @@ impl DirectXRenderer {
) )
.unwrap(); .unwrap();
} }
// let backbuffer = set_render_target_view(
// &self.context.swap_chain,
// &self.devices.device,
// &self.devices.device_context,
// )?;
let (render_target, render_target_view) = let (render_target, render_target_view) =
create_render_target_and_its_view(&self.context.swap_chain, &self.devices.device) create_render_target_and_its_view(&self.context.swap_chain, &self.devices.device)
.unwrap(); .unwrap();
@ -248,31 +242,6 @@ impl DirectXRenderer {
} }
// #[cfg(not(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( pub(crate) fn update_transparency(
&mut self, &mut self,
background_appearance: WindowBackgroundAppearance, background_appearance: WindowBackgroundAppearance,
@ -281,33 +250,84 @@ impl DirectXRenderer {
if self.transparent == transparent { if self.transparent == transparent {
return Ok(()); return Ok(());
} }
self.transparent = transparent; // self.transparent = transparent;
// unsafe { // let (width, height) = unsafe {
// // recreate the swapchain
// self.devices.device_context.OMSetRenderTargets(None, None); // self.devices.device_context.OMSetRenderTargets(None, None);
// drop(self.context.back_buffer[0].take().unwrap()); // ManuallyDrop::drop(&mut self.context.render_target);
// drop(self.context.render_target_view[0].take().unwrap());
// let desc = self.context.swap_chain.GetDesc1().unwrap();
// ManuallyDrop::drop(&mut self.context.swap_chain); // ManuallyDrop::drop(&mut self.context.swap_chain);
// self.context.swap_chain = create_swap_chain_default( // (desc.Width, desc.Height)
// &self.devices.dxgi_factory, // };
// &self.devices.device, // self.context.swap_chain = create_swap_chain(
// self.hwnd, // &self.devices.dxgi_factory,
// transparent, // &self.devices.device,
// )?; // transparent,
// self.context.back_buffer = [Some(set_render_target_view( // width,
// &self.context.swap_chain, // height,
// &self.devices.device, // )
// &self.devices.device_context, // .unwrap();
// )?)]; // self.context
// self.context.viewport = set_viewport( // .direct_composition
// &self.devices.device_context, // .set_swap_chain(&self.context.swap_chain)
// self.context.viewport[0].Width, // .context("Failed to set swap chain for DirectComposition")?;
// self.context.viewport[0].Height, // let (render_target, render_target_view) =
// ); // create_render_target_and_its_view(&self.context.swap_chain, &self.devices.device)
// set_rasterizer_state(&self.devices.device, &self.devices.device_context)?; // .unwrap();
// self.context.render_target = render_target;
// self.context.render_target_view = render_target_view;
// unsafe {
// self.devices
// .device_context
// .OMSetRenderTargets(Some(&self.context.render_target_view), None);
// } // }
// let (msaa_target, msaa_view) =
// create_msaa_target_and_its_view(&self.devices.device, width, height)?;
// self.context.msaa_target = msaa_target;
// self.context.msaa_view = msaa_view;
// self.context.viewport = set_viewport(&self.devices.device_context, width as _, height as _);
// set_rasterizer_state(&self.devices.device, &self.devices.device_context)?;
Ok(()) Ok(())
} }
// #[cfg(feature = "enable-renderdoc")]
// pub(crate) fn update_transparency(
// &mut self,
// background_appearance: WindowBackgroundAppearance,
// ) -> Result<()> {
// let transparent = background_appearance != WindowBackgroundAppearance::Opaque;
// if self.transparent == transparent {
// return Ok(());
// }
// self.transparent = transparent;
// unsafe {
// // recreate the swapchain
// self.devices.device_context.OMSetRenderTargets(None, None);
// drop(self.context.back_buffer[0].take().unwrap());
// ManuallyDrop::drop(&mut self.context.swap_chain);
// self.context.swap_chain = create_swap_chain_default(
// &self.devices.dxgi_factory,
// &self.devices.device,
// self.hwnd,
// transparent,
// )?;
// self.context.back_buffer = [Some(set_render_target_view(
// &self.context.swap_chain,
// &self.devices.device,
// &self.devices.device_context,
// )?)];
// self.context.viewport = set_viewport(
// &self.devices.device_context,
// self.context.viewport[0].Width,
// self.context.viewport[0].Height,
// );
// set_rasterizer_state(&self.devices.device, &self.devices.device_context)?;
// }
// Ok(())
// }
fn draw_shadows(&mut self, shadows: &[Shadow]) -> Result<()> { fn draw_shadows(&mut self, shadows: &[Shadow]) -> Result<()> {
if shadows.is_empty() { if shadows.is_empty() {
return Ok(()); return Ok(());
@ -461,24 +481,35 @@ impl DirectXRenderer {
impl DirectXContext { impl DirectXContext {
pub fn new(devices: &DirectXDevices, hwnd: HWND, transparent: bool) -> Result<Self> { pub fn new(devices: &DirectXDevices, hwnd: HWND, transparent: bool) -> Result<Self> {
let (width, height) = unsafe {
let mut rect = std::mem::zeroed();
GetWindowRect(hwnd, &mut rect)?;
(rect.right - rect.left, rect.bottom - rect.top)
};
assert!(
width > 0 && height > 0,
"Window size must be greater than zero"
);
// #[cfg(not(feature = "enable-renderdoc"))] // #[cfg(not(feature = "enable-renderdoc"))]
// let swap_chain = create_swap_chain(&devices.dxgi_factory, &devices.device, transparent)?; let swap_chain = create_swap_chain(
&devices.dxgi_factory,
&devices.device,
transparent,
width as u32,
height as u32,
)?;
// #[cfg(feature = "enable-renderdoc")] // #[cfg(feature = "enable-renderdoc")]
let swap_chain = // let swap_chain =
create_swap_chain_default(&devices.dxgi_factory, &devices.device, hwnd, transparent)?; // create_swap_chain_default(&devices.dxgi_factory, &devices.device, hwnd, transparent)?;
// #[cfg(not(feature = "enable-renderdoc"))] // #[cfg(not(feature = "enable-renderdoc"))]
// let direct_composition = DirectComposition::new(&devices.dxgi_device, hwnd)?; let direct_composition = DirectComposition::new(&devices.dxgi_device, hwnd)?;
// #[cfg(not(feature = "enable-renderdoc"))] // #[cfg(not(feature = "enable-renderdoc"))]
// direct_composition.set_swap_chain(&swap_chain)?; direct_composition.set_swap_chain(&swap_chain)?;
let (render_target, render_target_view) = let (render_target, render_target_view) =
create_render_target_and_its_view(&swap_chain, &devices.device)?; create_render_target_and_its_view(&swap_chain, &devices.device)?;
// let back_buffer = [Some(set_render_target_view( let (msaa_target, msaa_view) =
// &swap_chain, create_msaa_target_and_its_view(&devices.device, width as u32, height as u32)?;
// &devices.device, let viewport = set_viewport(&devices.device_context, width as f32, height as f32);
// &devices.device_context,
// )?)];
let (msaa_target, msaa_view) = create_msaa_target_and_its_view(&devices.device, 1, 1)?;
let viewport = set_viewport(&devices.device_context, 1.0, 1.0);
unsafe { unsafe {
devices devices
.device_context .device_context
@ -494,7 +525,7 @@ impl DirectXContext {
msaa_view, msaa_view,
viewport, viewport,
// #[cfg(not(feature = "enable-renderdoc"))] // #[cfg(not(feature = "enable-renderdoc"))]
// direct_composition, direct_composition,
}) })
} }
} }
@ -545,28 +576,28 @@ impl DirectXRenderPipelines {
} }
// #[cfg(not(feature = "enable-renderdoc"))] // #[cfg(not(feature = "enable-renderdoc"))]
// impl DirectComposition { impl DirectComposition {
// pub fn new(dxgi_device: &IDXGIDevice, hwnd: HWND) -> Result<Self> { pub fn new(dxgi_device: &IDXGIDevice, hwnd: HWND) -> Result<Self> {
// let comp_device = get_comp_device(&dxgi_device)?; let comp_device = get_comp_device(&dxgi_device)?;
// let comp_target = unsafe { comp_device.CreateTargetForHwnd(hwnd, true) }?; let comp_target = unsafe { comp_device.CreateTargetForHwnd(hwnd, true) }?;
// let comp_visual = unsafe { comp_device.CreateVisual() }?; let comp_visual = unsafe { comp_device.CreateVisual() }?;
// Ok(Self { Ok(Self {
// comp_device, comp_device,
// comp_target, comp_target,
// comp_visual, comp_visual,
// }) })
// } }
// pub fn set_swap_chain(&self, swap_chain: &IDXGISwapChain1) -> Result<()> { pub fn set_swap_chain(&self, swap_chain: &IDXGISwapChain1) -> Result<()> {
// unsafe { unsafe {
// self.comp_visual.SetContent(swap_chain)?; self.comp_visual.SetContent(swap_chain)?;
// self.comp_target.SetRoot(&self.comp_visual)?; self.comp_target.SetRoot(&self.comp_visual)?;
// self.comp_device.Commit()?; self.comp_device.Commit()?;
// } }
// Ok(()) Ok(())
// } }
// } }
impl DirectXGlobalElements { impl DirectXGlobalElements {
pub fn new(device: &ID3D11Device) -> Result<Self> { pub fn new(device: &ID3D11Device) -> Result<Self> {
@ -1030,39 +1061,45 @@ fn get_device(
} }
// #[cfg(not(feature = "enable-renderdoc"))] // #[cfg(not(feature = "enable-renderdoc"))]
// fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result<IDCompositionDevice> { fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result<IDCompositionDevice> {
// Ok(unsafe { DCompositionCreateDevice(dxgi_device)? }) Ok(unsafe { DCompositionCreateDevice(dxgi_device)? })
// } }
// fn create_swap_chain( fn create_swap_chain(
// dxgi_factory: &IDXGIFactory6, dxgi_factory: &IDXGIFactory6,
// device: &ID3D11Device, device: &ID3D11Device,
// transparent: bool, transparent: bool,
// ) -> Result<IDXGISwapChain1> { width: u32,
// let alpha_mode = if transparent { height: u32,
// DXGI_ALPHA_MODE_PREMULTIPLIED ) -> Result<ManuallyDrop<IDXGISwapChain1>> {
// } else { println!("Creating swap chain for DirectComposition: {}", transparent);
// DXGI_ALPHA_MODE_IGNORE let transparent = true;
// }; let alpha_mode = if transparent {
// let desc = DXGI_SWAP_CHAIN_DESC1 { DXGI_ALPHA_MODE_PREMULTIPLIED
// Width: 1, } else {
// Height: 1, DXGI_ALPHA_MODE_IGNORE
// Format: DXGI_FORMAT_B8G8R8A8_UNORM, };
// Stereo: false.into(), let desc = DXGI_SWAP_CHAIN_DESC1 {
// SampleDesc: DXGI_SAMPLE_DESC { Width: width,
// Count: 1, Height: height,
// Quality: 0, Format: RENDER_TARGET_FORMAT,
// }, Stereo: false.into(),
// BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT, SampleDesc: DXGI_SAMPLE_DESC {
// BufferCount: BUFFER_COUNT as u32, Count: 1,
// // Composition SwapChains only support the DXGI_SCALING_STRETCH Scaling. Quality: 0,
// Scaling: DXGI_SCALING_STRETCH, },
// SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
// AlphaMode: alpha_mode, BufferCount: BUFFER_COUNT as u32,
// Flags: 0, // Composition SwapChains only support the DXGI_SCALING_STRETCH Scaling.
// }; Scaling: DXGI_SCALING_STRETCH,
// Ok(unsafe { dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? }) SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
// } AlphaMode: alpha_mode,
Flags: 0,
};
Ok(ManuallyDrop::new(unsafe {
dxgi_factory.CreateSwapChainForComposition(device, &desc, None)?
}))
}
// #[cfg(feature = "enable-renderdoc")] // #[cfg(feature = "enable-renderdoc")]
fn create_swap_chain_default( fn create_swap_chain_default(
@ -1289,33 +1326,6 @@ fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: usize) -> Res
Ok(buffer.unwrap()) Ok(buffer.unwrap())
} }
#[inline]
fn pre_draw(
device_context: &ID3D11DeviceContext,
global_params_buffer: &[Option<ID3D11Buffer>; 1],
view_port: &[D3D11_VIEWPORT; 1],
render_target_view: &[Option<ID3D11RenderTargetView>; 1],
clear_color: [f32; 4],
blend_state: &ID3D11BlendState,
) -> Result<()> {
let global_params = global_params_buffer[0].as_ref().unwrap();
update_buffer(
device_context,
global_params,
&[GlobalParams {
viewport_size: [view_port[0].Width, view_port[0].Height],
..Default::default()
}],
)?;
unsafe {
device_context.RSSetViewports(Some(view_port));
device_context.OMSetRenderTargets(Some(render_target_view), None);
device_context.ClearRenderTargetView(render_target_view[0].as_ref().unwrap(), &clear_color);
device_context.OMSetBlendState(blend_state, None, 0xFFFFFFFF);
}
Ok(())
}
#[inline] #[inline]
fn update_buffer<T>( fn update_buffer<T>(
device_context: &ID3D11DeviceContext, device_context: &ID3D11DeviceContext,

View file

@ -384,7 +384,8 @@ impl WindowsWindow {
(WS_EX_TOOLWINDOW | WS_EX_LAYERED, WINDOW_STYLE(0x0)) (WS_EX_TOOLWINDOW | WS_EX_LAYERED, WINDOW_STYLE(0x0))
} else { } else {
( (
WS_EX_APPWINDOW | WS_EX_LAYERED, WS_EX_APPWINDOW | WS_EX_NOREDIRECTIONBITMAP,
// WS_EX_APPWINDOW | WS_EX_LAYERED,
WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX, WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX,
) )
}; };
@ -461,7 +462,7 @@ impl WindowsWindow {
// window is going to be composited with per-pixel alpha, but the render // window is going to be composited with per-pixel alpha, but the render
// pipeline is responsible for effectively calling UpdateLayeredWindow // pipeline is responsible for effectively calling UpdateLayeredWindow
// at the appropriate time. // at the appropriate time.
unsafe { SetLayeredWindowAttributes(hwnd, COLORREF(0), 255, LWA_ALPHA)? }; // unsafe { SetLayeredWindowAttributes(hwnd, COLORREF(0), 255, LWA_ALPHA)? };
Ok(Self(state_ptr)) Ok(Self(state_ptr))
} }