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_Direct3D11",
"Win32_Graphics_Direct3D_Fxc",
"Win32_Graphics_DirectComposition",
"Win32_Graphics_DirectWrite",
"Win32_Graphics_Dwm",
"Win32_Graphics_Dxgi",

View file

@ -3,7 +3,8 @@ use std::{mem::ManuallyDrop, sync::Arc};
use ::util::ResultExt;
use anyhow::{Context, Result};
// #[cfg(not(feature = "enable-renderdoc"))]
// use windows::Win32::Graphics::DirectComposition::*;
use windows::Win32::Graphics::DirectComposition::*;
use windows::Win32::UI::WindowsAndMessaging::GetWindowRect;
use windows::{
Win32::{
Foundation::{HMODULE, HWND},
@ -26,7 +27,6 @@ pub(crate) struct DirectXRenderer {
context: DirectXContext,
globals: DirectXGlobalElements,
pipelines: DirectXRenderPipelines,
hwnd: HWND,
transparent: bool,
}
@ -46,7 +46,7 @@ struct DirectXContext {
msaa_view: ID3D11RenderTargetView,
viewport: [D3D11_VIEWPORT; 1],
// #[cfg(not(feature = "enable-renderdoc"))]
// direct_composition: DirectComposition,
direct_composition: DirectComposition,
}
struct DirectXRenderPipelines {
@ -73,11 +73,11 @@ struct DrawInstancedIndirectArgs {
}
// #[cfg(not(feature = "enable-renderdoc"))]
// struct DirectComposition {
// comp_device: IDCompositionDevice,
// comp_target: IDCompositionTarget,
// comp_visual: IDCompositionVisual,
// }
struct DirectComposition {
comp_device: IDCompositionDevice,
comp_target: IDCompositionTarget,
comp_visual: IDCompositionVisual,
}
impl DirectXDevices {
pub(crate) fn new() -> Result<Self> {
@ -115,7 +115,6 @@ impl DirectXRenderer {
context,
globals,
pipelines,
hwnd,
transparent,
})
}
@ -215,11 +214,6 @@ impl DirectXRenderer {
)
.unwrap();
}
// let backbuffer = set_render_target_view(
// &self.context.swap_chain,
// &self.devices.device,
// &self.devices.device_context,
// )?;
let (render_target, render_target_view) =
create_render_target_and_its_view(&self.context.swap_chain, &self.devices.device)
.unwrap();
@ -248,31 +242,6 @@ impl DirectXRenderer {
}
// #[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,
@ -281,33 +250,84 @@ impl DirectXRenderer {
if self.transparent == transparent {
return Ok(());
}
self.transparent = transparent;
// unsafe {
// // recreate the swapchain
// self.transparent = transparent;
// let (width, height) = unsafe {
// 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);
// 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)?;
// (desc.Width, desc.Height)
// };
// self.context.swap_chain = create_swap_chain(
// &self.devices.dxgi_factory,
// &self.devices.device,
// transparent,
// width,
// height,
// )
// .unwrap();
// self.context
// .direct_composition
// .set_swap_chain(&self.context.swap_chain)
// .context("Failed to set swap chain for DirectComposition")?;
// let (render_target, render_target_view) =
// create_render_target_and_its_view(&self.context.swap_chain, &self.devices.device)
// .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(())
}
// #[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<()> {
if shadows.is_empty() {
return Ok(());
@ -461,24 +481,35 @@ impl DirectXRenderer {
impl DirectXContext {
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"))]
// 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")]
let swap_chain =
create_swap_chain_default(&devices.dxgi_factory, &devices.device, hwnd, transparent)?;
// 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)?;
let direct_composition = DirectComposition::new(&devices.dxgi_device, hwnd)?;
// #[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) =
create_render_target_and_its_view(&swap_chain, &devices.device)?;
// let back_buffer = [Some(set_render_target_view(
// &swap_chain,
// &devices.device,
// &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);
let (msaa_target, msaa_view) =
create_msaa_target_and_its_view(&devices.device, width as u32, height as u32)?;
let viewport = set_viewport(&devices.device_context, width as f32, height as f32);
unsafe {
devices
.device_context
@ -494,7 +525,7 @@ impl DirectXContext {
msaa_view,
viewport,
// #[cfg(not(feature = "enable-renderdoc"))]
// direct_composition,
direct_composition,
})
}
}
@ -545,28 +576,28 @@ 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() }?;
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> {
@ -1030,39 +1061,45 @@ fn get_device(
}
// #[cfg(not(feature = "enable-renderdoc"))]
// fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result<IDCompositionDevice> {
// Ok(unsafe { DCompositionCreateDevice(dxgi_device)? })
// }
fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result<IDCompositionDevice> {
Ok(unsafe { DCompositionCreateDevice(dxgi_device)? })
}
// fn create_swap_chain(
// dxgi_factory: &IDXGIFactory6,
// device: &ID3D11Device,
// transparent: bool,
// ) -> Result<IDXGISwapChain1> {
// let alpha_mode = if transparent {
// DXGI_ALPHA_MODE_PREMULTIPLIED
// } else {
// DXGI_ALPHA_MODE_IGNORE
// };
// let desc = DXGI_SWAP_CHAIN_DESC1 {
// Width: 1,
// Height: 1,
// Format: DXGI_FORMAT_B8G8R8A8_UNORM,
// Stereo: false.into(),
// SampleDesc: DXGI_SAMPLE_DESC {
// Count: 1,
// Quality: 0,
// },
// BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
// BufferCount: BUFFER_COUNT as u32,
// // Composition SwapChains only support the DXGI_SCALING_STRETCH Scaling.
// Scaling: DXGI_SCALING_STRETCH,
// SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
// AlphaMode: alpha_mode,
// Flags: 0,
// };
// Ok(unsafe { dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? })
// }
fn create_swap_chain(
dxgi_factory: &IDXGIFactory6,
device: &ID3D11Device,
transparent: bool,
width: u32,
height: u32,
) -> Result<ManuallyDrop<IDXGISwapChain1>> {
println!("Creating swap chain for DirectComposition: {}", transparent);
let transparent = true;
let alpha_mode = if transparent {
DXGI_ALPHA_MODE_PREMULTIPLIED
} else {
DXGI_ALPHA_MODE_IGNORE
};
let desc = DXGI_SWAP_CHAIN_DESC1 {
Width: width,
Height: height,
Format: RENDER_TARGET_FORMAT,
Stereo: false.into(),
SampleDesc: DXGI_SAMPLE_DESC {
Count: 1,
Quality: 0,
},
BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
BufferCount: BUFFER_COUNT as u32,
// Composition SwapChains only support the DXGI_SCALING_STRETCH Scaling.
Scaling: DXGI_SCALING_STRETCH,
SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
AlphaMode: alpha_mode,
Flags: 0,
};
Ok(ManuallyDrop::new(unsafe {
dxgi_factory.CreateSwapChainForComposition(device, &desc, None)?
}))
}
// #[cfg(feature = "enable-renderdoc")]
fn create_swap_chain_default(
@ -1289,33 +1326,6 @@ fn create_indirect_draw_buffer(device: &ID3D11Device, buffer_size: usize) -> Res
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]
fn update_buffer<T>(
device_context: &ID3D11DeviceContext,

View file

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