windows: Keep just one copy of GPU instance (#37445)
Now we only keep a single copy of the GPU device. The GPU lost handling got broken after #35376, but it’s properly handled again now. Release Notes: - N/A
This commit is contained in:
@@ -2,6 +2,7 @@ mod clipboard;
|
||||
mod destination_list;
|
||||
mod direct_write;
|
||||
mod directx_atlas;
|
||||
mod directx_devices;
|
||||
mod directx_renderer;
|
||||
mod dispatcher;
|
||||
mod display;
|
||||
@@ -18,6 +19,7 @@ pub(crate) use clipboard::*;
|
||||
pub(crate) use destination_list::*;
|
||||
pub(crate) use direct_write::*;
|
||||
pub(crate) use directx_atlas::*;
|
||||
pub(crate) use directx_devices::*;
|
||||
pub(crate) use directx_renderer::*;
|
||||
pub(crate) use dispatcher::*;
|
||||
pub(crate) use display::*;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::{borrow::Cow, sync::Arc};
|
||||
|
||||
use ::util::ResultExt;
|
||||
use anyhow::Result;
|
||||
use anyhow::{Context, Result};
|
||||
use collections::HashMap;
|
||||
use itertools::Itertools;
|
||||
use parking_lot::{RwLock, RwLockUpgradableReadGuard};
|
||||
@@ -70,7 +70,7 @@ struct FontIdentifier {
|
||||
}
|
||||
|
||||
impl DirectWriteComponent {
|
||||
pub fn new(gpu_context: &DirectXDevices) -> Result<Self> {
|
||||
pub fn new(directx_devices: &DirectXDevices) -> Result<Self> {
|
||||
// todo: ideally this would not be a large unsafe block but smaller isolated ones for easier auditing
|
||||
unsafe {
|
||||
let factory: IDWriteFactory5 = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED)?;
|
||||
@@ -85,7 +85,7 @@ impl DirectWriteComponent {
|
||||
let locale = String::from_utf16_lossy(&locale_vec);
|
||||
let text_renderer = Arc::new(TextRendererWrapper::new(&locale));
|
||||
|
||||
let gpu_state = GPUState::new(gpu_context)?;
|
||||
let gpu_state = GPUState::new(directx_devices)?;
|
||||
|
||||
Ok(DirectWriteComponent {
|
||||
locale,
|
||||
@@ -100,9 +100,9 @@ impl DirectWriteComponent {
|
||||
}
|
||||
|
||||
impl GPUState {
|
||||
fn new(gpu_context: &DirectXDevices) -> Result<Self> {
|
||||
let device = gpu_context.device.clone();
|
||||
let device_context = gpu_context.device_context.clone();
|
||||
fn new(directx_devices: &DirectXDevices) -> Result<Self> {
|
||||
let device = directx_devices.device.clone();
|
||||
let device_context = directx_devices.device_context.clone();
|
||||
|
||||
let blend_state = {
|
||||
let mut blend_state = None;
|
||||
@@ -183,8 +183,8 @@ impl GPUState {
|
||||
}
|
||||
|
||||
impl DirectWriteTextSystem {
|
||||
pub(crate) fn new(gpu_context: &DirectXDevices) -> Result<Self> {
|
||||
let components = DirectWriteComponent::new(gpu_context)?;
|
||||
pub(crate) fn new(directx_devices: &DirectXDevices) -> Result<Self> {
|
||||
let components = DirectWriteComponent::new(directx_devices)?;
|
||||
let system_font_collection = unsafe {
|
||||
let mut result = std::mem::zeroed();
|
||||
components
|
||||
@@ -210,6 +210,10 @@ impl DirectWriteTextSystem {
|
||||
font_id_by_identifier: HashMap::default(),
|
||||
})))
|
||||
}
|
||||
|
||||
pub(crate) fn handle_gpu_lost(&self, directx_devices: &DirectXDevices) {
|
||||
self.0.write().handle_gpu_lost(directx_devices);
|
||||
}
|
||||
}
|
||||
|
||||
impl PlatformTextSystem for DirectWriteTextSystem {
|
||||
@@ -1211,6 +1215,20 @@ impl DirectWriteState {
|
||||
));
|
||||
result
|
||||
}
|
||||
|
||||
fn handle_gpu_lost(&mut self, directx_devices: &DirectXDevices) {
|
||||
try_to_recover_from_device_lost(
|
||||
|| GPUState::new(directx_devices).context("Recreating GPU state for DirectWrite"),
|
||||
|gpu_state| self.components.gpu_state = gpu_state,
|
||||
|| {
|
||||
log::error!(
|
||||
"Failed to recreate GPU state for DirectWrite after multiple attempts."
|
||||
);
|
||||
// Do something here?
|
||||
// At this point, the device loss is considered unrecoverable.
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DirectWriteState {
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
use anyhow::{Context, Result};
|
||||
use util::ResultExt;
|
||||
use windows::Win32::{
|
||||
Foundation::HMODULE,
|
||||
Graphics::{
|
||||
Direct3D::{
|
||||
D3D_DRIVER_TYPE_UNKNOWN, D3D_FEATURE_LEVEL, D3D_FEATURE_LEVEL_10_1,
|
||||
D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1,
|
||||
},
|
||||
Direct3D11::{
|
||||
D3D11_CREATE_DEVICE_BGRA_SUPPORT, D3D11_CREATE_DEVICE_DEBUG, D3D11_SDK_VERSION,
|
||||
D3D11CreateDevice, ID3D11Device, ID3D11DeviceContext,
|
||||
},
|
||||
Dxgi::{
|
||||
CreateDXGIFactory2, DXGI_CREATE_FACTORY_DEBUG, DXGI_CREATE_FACTORY_FLAGS,
|
||||
DXGI_GPU_PREFERENCE_MINIMUM_POWER, IDXGIAdapter1, IDXGIFactory6,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
pub(crate) fn try_to_recover_from_device_lost<T>(
|
||||
mut f: impl FnMut() -> Result<T>,
|
||||
on_success: impl FnOnce(T),
|
||||
on_error: impl FnOnce(),
|
||||
) {
|
||||
let result = (0..5).find_map(|i| {
|
||||
if i > 0 {
|
||||
// Add a small delay before retrying
|
||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||
}
|
||||
f().log_err()
|
||||
});
|
||||
|
||||
if let Some(result) = result {
|
||||
on_success(result);
|
||||
} else {
|
||||
on_error();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct DirectXDevices {
|
||||
pub(crate) adapter: IDXGIAdapter1,
|
||||
pub(crate) dxgi_factory: IDXGIFactory6,
|
||||
pub(crate) device: ID3D11Device,
|
||||
pub(crate) device_context: ID3D11DeviceContext,
|
||||
}
|
||||
|
||||
impl DirectXDevices {
|
||||
pub(crate) fn new() -> Result<Self> {
|
||||
let debug_layer_available = check_debug_layer_available();
|
||||
let dxgi_factory =
|
||||
get_dxgi_factory(debug_layer_available).context("Creating DXGI factory")?;
|
||||
let adapter =
|
||||
get_adapter(&dxgi_factory, debug_layer_available).context("Getting DXGI adapter")?;
|
||||
let (device, device_context) = {
|
||||
let mut device: Option<ID3D11Device> = None;
|
||||
let mut context: Option<ID3D11DeviceContext> = None;
|
||||
let mut feature_level = D3D_FEATURE_LEVEL::default();
|
||||
get_device(
|
||||
&adapter,
|
||||
Some(&mut device),
|
||||
Some(&mut context),
|
||||
Some(&mut feature_level),
|
||||
debug_layer_available,
|
||||
)
|
||||
.context("Creating Direct3D device")?;
|
||||
match feature_level {
|
||||
D3D_FEATURE_LEVEL_11_1 => {
|
||||
log::info!("Created device with Direct3D 11.1 feature level.")
|
||||
}
|
||||
D3D_FEATURE_LEVEL_11_0 => {
|
||||
log::info!("Created device with Direct3D 11.0 feature level.")
|
||||
}
|
||||
D3D_FEATURE_LEVEL_10_1 => {
|
||||
log::info!("Created device with Direct3D 10.1 feature level.")
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
(device.unwrap(), context.unwrap())
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
adapter,
|
||||
dxgi_factory,
|
||||
device,
|
||||
device_context,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_debug_layer_available() -> bool {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
use windows::Win32::Graphics::Dxgi::{DXGIGetDebugInterface1, IDXGIInfoQueue};
|
||||
|
||||
unsafe { DXGIGetDebugInterface1::<IDXGIInfoQueue>(0) }
|
||||
.log_err()
|
||||
.is_some()
|
||||
}
|
||||
#[cfg(not(debug_assertions))]
|
||||
{
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_dxgi_factory(debug_layer_available: bool) -> Result<IDXGIFactory6> {
|
||||
let factory_flag = if debug_layer_available {
|
||||
DXGI_CREATE_FACTORY_DEBUG
|
||||
} else {
|
||||
#[cfg(debug_assertions)]
|
||||
log::warn!(
|
||||
"Failed to get DXGI debug interface. DirectX debugging features will be disabled."
|
||||
);
|
||||
DXGI_CREATE_FACTORY_FLAGS::default()
|
||||
};
|
||||
unsafe { Ok(CreateDXGIFactory2(factory_flag)?) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_adapter(dxgi_factory: &IDXGIFactory6, debug_layer_available: bool) -> Result<IDXGIAdapter1> {
|
||||
for adapter_index in 0.. {
|
||||
let adapter: IDXGIAdapter1 = unsafe {
|
||||
dxgi_factory
|
||||
.EnumAdapterByGpuPreference(adapter_index, DXGI_GPU_PREFERENCE_MINIMUM_POWER)
|
||||
}?;
|
||||
if let Ok(desc) = unsafe { adapter.GetDesc1() } {
|
||||
let gpu_name = String::from_utf16_lossy(&desc.Description)
|
||||
.trim_matches(char::from(0))
|
||||
.to_string();
|
||||
log::info!("Using GPU: {}", gpu_name);
|
||||
}
|
||||
// Check to see whether the adapter supports Direct3D 11, but don't
|
||||
// create the actual device yet.
|
||||
if get_device(&adapter, None, None, None, debug_layer_available)
|
||||
.log_err()
|
||||
.is_some()
|
||||
{
|
||||
return Ok(adapter);
|
||||
}
|
||||
}
|
||||
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_device(
|
||||
adapter: &IDXGIAdapter1,
|
||||
device: Option<*mut Option<ID3D11Device>>,
|
||||
context: Option<*mut Option<ID3D11DeviceContext>>,
|
||||
feature_level: Option<*mut D3D_FEATURE_LEVEL>,
|
||||
debug_layer_available: bool,
|
||||
) -> Result<()> {
|
||||
let device_flags = if debug_layer_available {
|
||||
D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG
|
||||
} else {
|
||||
D3D11_CREATE_DEVICE_BGRA_SUPPORT
|
||||
};
|
||||
unsafe {
|
||||
D3D11CreateDevice(
|
||||
adapter,
|
||||
D3D_DRIVER_TYPE_UNKNOWN,
|
||||
HMODULE::default(),
|
||||
device_flags,
|
||||
// 4x MSAA is required for Direct3D Feature Level 10.1 or better
|
||||
Some(&[
|
||||
D3D_FEATURE_LEVEL_11_1,
|
||||
D3D_FEATURE_LEVEL_11_0,
|
||||
D3D_FEATURE_LEVEL_10_1,
|
||||
]),
|
||||
D3D11_SDK_VERSION,
|
||||
device,
|
||||
feature_level,
|
||||
context,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -7,7 +7,7 @@ use ::util::ResultExt;
|
||||
use anyhow::{Context, Result};
|
||||
use windows::{
|
||||
Win32::{
|
||||
Foundation::{HMODULE, HWND},
|
||||
Foundation::HWND,
|
||||
Graphics::{
|
||||
Direct3D::*,
|
||||
Direct3D11::*,
|
||||
@@ -39,7 +39,7 @@ pub(crate) struct FontInfo {
|
||||
pub(crate) struct DirectXRenderer {
|
||||
hwnd: HWND,
|
||||
atlas: Arc<DirectXAtlas>,
|
||||
devices: ManuallyDrop<DirectXDevices>,
|
||||
devices: ManuallyDrop<DirectXRendererDevices>,
|
||||
resources: ManuallyDrop<DirectXResources>,
|
||||
globals: DirectXGlobalElements,
|
||||
pipelines: DirectXRenderPipelines,
|
||||
@@ -49,9 +49,9 @@ pub(crate) struct DirectXRenderer {
|
||||
|
||||
/// Direct3D objects
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct DirectXDevices {
|
||||
adapter: IDXGIAdapter1,
|
||||
dxgi_factory: IDXGIFactory6,
|
||||
pub(crate) struct DirectXRendererDevices {
|
||||
pub(crate) adapter: IDXGIAdapter1,
|
||||
pub(crate) dxgi_factory: IDXGIFactory6,
|
||||
pub(crate) device: ID3D11Device,
|
||||
pub(crate) device_context: ID3D11DeviceContext,
|
||||
dxgi_device: Option<IDXGIDevice>,
|
||||
@@ -96,39 +96,17 @@ struct DirectComposition {
|
||||
comp_visual: IDCompositionVisual,
|
||||
}
|
||||
|
||||
impl DirectXDevices {
|
||||
pub(crate) fn new(disable_direct_composition: bool) -> Result<ManuallyDrop<Self>> {
|
||||
let debug_layer_available = check_debug_layer_available();
|
||||
let dxgi_factory =
|
||||
get_dxgi_factory(debug_layer_available).context("Creating DXGI factory")?;
|
||||
let adapter =
|
||||
get_adapter(&dxgi_factory, debug_layer_available).context("Getting DXGI adapter")?;
|
||||
let (device, device_context) = {
|
||||
let mut device: Option<ID3D11Device> = None;
|
||||
let mut context: Option<ID3D11DeviceContext> = None;
|
||||
let mut feature_level = D3D_FEATURE_LEVEL::default();
|
||||
get_device(
|
||||
&adapter,
|
||||
Some(&mut device),
|
||||
Some(&mut context),
|
||||
Some(&mut feature_level),
|
||||
debug_layer_available,
|
||||
)
|
||||
.context("Creating Direct3D device")?;
|
||||
match feature_level {
|
||||
D3D_FEATURE_LEVEL_11_1 => {
|
||||
log::info!("Created device with Direct3D 11.1 feature level.")
|
||||
}
|
||||
D3D_FEATURE_LEVEL_11_0 => {
|
||||
log::info!("Created device with Direct3D 11.0 feature level.")
|
||||
}
|
||||
D3D_FEATURE_LEVEL_10_1 => {
|
||||
log::info!("Created device with Direct3D 10.1 feature level.")
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
(device.unwrap(), context.unwrap())
|
||||
};
|
||||
impl DirectXRendererDevices {
|
||||
pub(crate) fn new(
|
||||
directx_devices: &DirectXDevices,
|
||||
disable_direct_composition: bool,
|
||||
) -> Result<ManuallyDrop<Self>> {
|
||||
let DirectXDevices {
|
||||
adapter,
|
||||
dxgi_factory,
|
||||
device,
|
||||
device_context,
|
||||
} = directx_devices;
|
||||
let dxgi_device = if disable_direct_composition {
|
||||
None
|
||||
} else {
|
||||
@@ -136,23 +114,27 @@ impl DirectXDevices {
|
||||
};
|
||||
|
||||
Ok(ManuallyDrop::new(Self {
|
||||
adapter,
|
||||
dxgi_factory,
|
||||
adapter: adapter.clone(),
|
||||
dxgi_factory: dxgi_factory.clone(),
|
||||
device: device.clone(),
|
||||
device_context: device_context.clone(),
|
||||
dxgi_device,
|
||||
device,
|
||||
device_context,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl DirectXRenderer {
|
||||
pub(crate) fn new(hwnd: HWND, disable_direct_composition: bool) -> Result<Self> {
|
||||
pub(crate) fn new(
|
||||
hwnd: HWND,
|
||||
directx_devices: &DirectXDevices,
|
||||
disable_direct_composition: bool,
|
||||
) -> Result<Self> {
|
||||
if disable_direct_composition {
|
||||
log::info!("Direct Composition is disabled.");
|
||||
}
|
||||
|
||||
let devices =
|
||||
DirectXDevices::new(disable_direct_composition).context("Creating DirectX devices")?;
|
||||
let devices = DirectXRendererDevices::new(directx_devices, disable_direct_composition)
|
||||
.context("Creating DirectX devices")?;
|
||||
let atlas = Arc::new(DirectXAtlas::new(&devices.device, &devices.device_context));
|
||||
|
||||
let resources = DirectXResources::new(&devices, 1, 1, hwnd, disable_direct_composition)
|
||||
@@ -218,28 +200,30 @@ impl DirectXRenderer {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn present(&mut self) -> Result<()> {
|
||||
unsafe {
|
||||
let result = self.resources.swap_chain.Present(0, DXGI_PRESENT(0));
|
||||
// Presenting the swap chain can fail if the DirectX device was removed or reset.
|
||||
if result == DXGI_ERROR_DEVICE_REMOVED || result == DXGI_ERROR_DEVICE_RESET {
|
||||
let reason = self.devices.device.GetDeviceRemovedReason();
|
||||
log::error!(
|
||||
"DirectX device removed or reset when drawing. Reason: {:?}",
|
||||
reason
|
||||
);
|
||||
self.handle_device_lost()?;
|
||||
} else {
|
||||
result.ok()?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
let result = unsafe { self.resources.swap_chain.Present(0, DXGI_PRESENT(0)) };
|
||||
result.ok().context("Presenting swap chain failed")
|
||||
}
|
||||
|
||||
fn handle_device_lost(&mut self) -> Result<()> {
|
||||
// Here we wait a bit to ensure the the system has time to recover from the device lost state.
|
||||
// If we don't wait, the final drawing result will be blank.
|
||||
std::thread::sleep(std::time::Duration::from_millis(300));
|
||||
pub(crate) fn handle_device_lost(&mut self, directx_devices: &DirectXDevices) {
|
||||
try_to_recover_from_device_lost(
|
||||
|| {
|
||||
self.handle_device_lost_impl(directx_devices)
|
||||
.context("DirectXRenderer handling device lost")
|
||||
},
|
||||
|_| {},
|
||||
|| {
|
||||
log::error!(
|
||||
"DirectXRenderer failed to recover from device lost after multiple attempts"
|
||||
);
|
||||
// Do something here?
|
||||
// At this point, the device loss is considered unrecoverable.
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn handle_device_lost_impl(&mut self, directx_devices: &DirectXDevices) -> Result<()> {
|
||||
let disable_direct_composition = self.direct_composition.is_none();
|
||||
|
||||
unsafe {
|
||||
@@ -262,7 +246,7 @@ impl DirectXRenderer {
|
||||
ManuallyDrop::drop(&mut self.devices);
|
||||
}
|
||||
|
||||
let devices = DirectXDevices::new(disable_direct_composition)
|
||||
let devices = DirectXRendererDevices::new(directx_devices, disable_direct_composition)
|
||||
.context("Recreating DirectX devices")?;
|
||||
let resources = DirectXResources::new(
|
||||
&devices,
|
||||
@@ -337,49 +321,39 @@ impl DirectXRenderer {
|
||||
if self.resources.width == width && self.resources.height == height {
|
||||
return Ok(());
|
||||
}
|
||||
self.resources.width = width;
|
||||
self.resources.height = height;
|
||||
|
||||
// Clear the render target before resizing
|
||||
unsafe { self.devices.device_context.OMSetRenderTargets(None, None) };
|
||||
unsafe { ManuallyDrop::drop(&mut self.resources.render_target) };
|
||||
drop(self.resources.render_target_view[0].take().unwrap());
|
||||
|
||||
// Resizing the swap chain requires a call to the underlying DXGI adapter, which can return the device removed error.
|
||||
// The app might have moved to a monitor that's attached to a different graphics device.
|
||||
// When a graphics device is removed or reset, the desktop resolution often changes, resulting in a window size change.
|
||||
// But here we just return the error, because we are handling device lost scenarios elsewhere.
|
||||
unsafe {
|
||||
// Clear the render target before resizing
|
||||
self.devices.device_context.OMSetRenderTargets(None, None);
|
||||
ManuallyDrop::drop(&mut self.resources.render_target);
|
||||
drop(self.resources.render_target_view[0].take().unwrap());
|
||||
|
||||
let result = self.resources.swap_chain.ResizeBuffers(
|
||||
BUFFER_COUNT as u32,
|
||||
width,
|
||||
height,
|
||||
RENDER_TARGET_FORMAT,
|
||||
DXGI_SWAP_CHAIN_FLAG(0),
|
||||
);
|
||||
// Resizing the swap chain requires a call to the underlying DXGI adapter, which can return the device removed error.
|
||||
// The app might have moved to a monitor that's attached to a different graphics device.
|
||||
// When a graphics device is removed or reset, the desktop resolution often changes, resulting in a window size change.
|
||||
match result {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
if e.code() == DXGI_ERROR_DEVICE_REMOVED || e.code() == DXGI_ERROR_DEVICE_RESET
|
||||
{
|
||||
let reason = self.devices.device.GetDeviceRemovedReason();
|
||||
log::error!(
|
||||
"DirectX device removed or reset when resizing. Reason: {:?}",
|
||||
reason
|
||||
);
|
||||
self.resources.width = width;
|
||||
self.resources.height = height;
|
||||
self.handle_device_lost()?;
|
||||
return Ok(());
|
||||
} else {
|
||||
log::error!("Failed to resize swap chain: {:?}", e);
|
||||
return Err(e.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.resources
|
||||
.recreate_resources(&self.devices, width, height)?;
|
||||
.swap_chain
|
||||
.ResizeBuffers(
|
||||
BUFFER_COUNT as u32,
|
||||
width,
|
||||
height,
|
||||
RENDER_TARGET_FORMAT,
|
||||
DXGI_SWAP_CHAIN_FLAG(0),
|
||||
)
|
||||
.context("Failed to resize swap chain")?;
|
||||
}
|
||||
|
||||
self.resources
|
||||
.recreate_resources(&self.devices, width, height)?;
|
||||
unsafe {
|
||||
self.devices
|
||||
.device_context
|
||||
.OMSetRenderTargets(Some(&self.resources.render_target_view), None);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -680,7 +654,7 @@ impl DirectXRenderer {
|
||||
|
||||
impl DirectXResources {
|
||||
pub fn new(
|
||||
devices: &DirectXDevices,
|
||||
devices: &DirectXRendererDevices,
|
||||
width: u32,
|
||||
height: u32,
|
||||
hwnd: HWND,
|
||||
@@ -725,7 +699,7 @@ impl DirectXResources {
|
||||
#[inline]
|
||||
fn recreate_resources(
|
||||
&mut self,
|
||||
devices: &DirectXDevices,
|
||||
devices: &DirectXRendererDevices,
|
||||
width: u32,
|
||||
height: u32,
|
||||
) -> Result<()> {
|
||||
@@ -745,8 +719,6 @@ impl DirectXResources {
|
||||
self.path_intermediate_msaa_view = path_intermediate_msaa_view;
|
||||
self.path_intermediate_srv = path_intermediate_srv;
|
||||
self.viewport = viewport;
|
||||
self.width = width;
|
||||
self.height = height;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1041,92 +1013,6 @@ impl Drop for DirectXResources {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_debug_layer_available() -> bool {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
unsafe { DXGIGetDebugInterface1::<IDXGIInfoQueue>(0) }
|
||||
.log_err()
|
||||
.is_some()
|
||||
}
|
||||
#[cfg(not(debug_assertions))]
|
||||
{
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_dxgi_factory(debug_layer_available: bool) -> Result<IDXGIFactory6> {
|
||||
let factory_flag = if debug_layer_available {
|
||||
DXGI_CREATE_FACTORY_DEBUG
|
||||
} else {
|
||||
#[cfg(debug_assertions)]
|
||||
log::warn!(
|
||||
"Failed to get DXGI debug interface. DirectX debugging features will be disabled."
|
||||
);
|
||||
DXGI_CREATE_FACTORY_FLAGS::default()
|
||||
};
|
||||
unsafe { Ok(CreateDXGIFactory2(factory_flag)?) }
|
||||
}
|
||||
|
||||
fn get_adapter(dxgi_factory: &IDXGIFactory6, debug_layer_available: bool) -> Result<IDXGIAdapter1> {
|
||||
for adapter_index in 0.. {
|
||||
let adapter: IDXGIAdapter1 = unsafe {
|
||||
dxgi_factory
|
||||
.EnumAdapterByGpuPreference(adapter_index, DXGI_GPU_PREFERENCE_MINIMUM_POWER)
|
||||
}?;
|
||||
if let Ok(desc) = unsafe { adapter.GetDesc1() } {
|
||||
let gpu_name = String::from_utf16_lossy(&desc.Description)
|
||||
.trim_matches(char::from(0))
|
||||
.to_string();
|
||||
log::info!("Using GPU: {}", gpu_name);
|
||||
}
|
||||
// Check to see whether the adapter supports Direct3D 11, but don't
|
||||
// create the actual device yet.
|
||||
if get_device(&adapter, None, None, None, debug_layer_available)
|
||||
.log_err()
|
||||
.is_some()
|
||||
{
|
||||
return Ok(adapter);
|
||||
}
|
||||
}
|
||||
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn get_device(
|
||||
adapter: &IDXGIAdapter1,
|
||||
device: Option<*mut Option<ID3D11Device>>,
|
||||
context: Option<*mut Option<ID3D11DeviceContext>>,
|
||||
feature_level: Option<*mut D3D_FEATURE_LEVEL>,
|
||||
debug_layer_available: bool,
|
||||
) -> Result<()> {
|
||||
let device_flags = if debug_layer_available {
|
||||
D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG
|
||||
} else {
|
||||
D3D11_CREATE_DEVICE_BGRA_SUPPORT
|
||||
};
|
||||
unsafe {
|
||||
D3D11CreateDevice(
|
||||
adapter,
|
||||
D3D_DRIVER_TYPE_UNKNOWN,
|
||||
HMODULE::default(),
|
||||
device_flags,
|
||||
// 4x MSAA is required for Direct3D Feature Level 10.1 or better
|
||||
Some(&[
|
||||
D3D_FEATURE_LEVEL_11_1,
|
||||
D3D_FEATURE_LEVEL_11_0,
|
||||
D3D_FEATURE_LEVEL_10_1,
|
||||
]),
|
||||
D3D11_SDK_VERSION,
|
||||
device,
|
||||
feature_level,
|
||||
context,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_comp_device(dxgi_device: &IDXGIDevice) -> Result<IDCompositionDevice> {
|
||||
Ok(unsafe { DCompositionCreateDevice(dxgi_device)? })
|
||||
@@ -1191,7 +1077,7 @@ fn create_swap_chain(
|
||||
|
||||
#[inline]
|
||||
fn create_resources(
|
||||
devices: &DirectXDevices,
|
||||
devices: &DirectXRendererDevices,
|
||||
swap_chain: &IDXGISwapChain1,
|
||||
width: u32,
|
||||
height: u32,
|
||||
|
||||
@@ -25,6 +25,7 @@ pub(crate) const WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD: u32 = WM_USER + 3;
|
||||
pub(crate) const WM_GPUI_DOCK_MENU_ACTION: u32 = WM_USER + 4;
|
||||
pub(crate) const WM_GPUI_FORCE_UPDATE_WINDOW: u32 = WM_USER + 5;
|
||||
pub(crate) const WM_GPUI_KEYBOARD_LAYOUT_CHANGED: u32 = WM_USER + 6;
|
||||
pub(crate) const WM_GPUI_GPU_DEVICE_LOST: u32 = WM_USER + 7;
|
||||
|
||||
const SIZE_MOVE_LOOP_TIMER_ID: usize = 1;
|
||||
const AUTO_HIDE_TASKBAR_THICKNESS_PX: i32 = 1;
|
||||
@@ -40,7 +41,6 @@ impl WindowsWindowInner {
|
||||
let handled = match msg {
|
||||
WM_ACTIVATE => self.handle_activate_msg(wparam),
|
||||
WM_CREATE => self.handle_create_msg(handle),
|
||||
WM_DEVICECHANGE => self.handle_device_change_msg(handle, wparam),
|
||||
WM_MOVE => self.handle_move_msg(handle, lparam),
|
||||
WM_SIZE => self.handle_size_msg(wparam, lparam),
|
||||
WM_GETMINMAXINFO => self.handle_get_min_max_info_msg(lparam),
|
||||
@@ -104,6 +104,7 @@ impl WindowsWindowInner {
|
||||
WM_SHOWWINDOW => self.handle_window_visibility_changed(handle, wparam),
|
||||
WM_GPUI_CURSOR_STYLE_CHANGED => self.handle_cursor_changed(lparam),
|
||||
WM_GPUI_FORCE_UPDATE_WINDOW => self.draw_window(handle, true),
|
||||
WM_GPUI_GPU_DEVICE_LOST => self.handle_device_lost(lparam),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(n) = handled {
|
||||
@@ -1167,26 +1168,12 @@ impl WindowsWindowInner {
|
||||
None
|
||||
}
|
||||
|
||||
fn handle_device_change_msg(&self, handle: HWND, wparam: WPARAM) -> Option<isize> {
|
||||
if wparam.0 == DBT_DEVNODES_CHANGED as usize {
|
||||
// The reason for sending this message is to actually trigger a redraw of the window.
|
||||
unsafe {
|
||||
PostMessageW(
|
||||
Some(handle),
|
||||
WM_GPUI_FORCE_UPDATE_WINDOW,
|
||||
WPARAM(0),
|
||||
LPARAM(0),
|
||||
)
|
||||
.log_err();
|
||||
}
|
||||
// If the GPU device is lost, this redraw will take care of recreating the device context.
|
||||
// The WM_GPUI_FORCE_UPDATE_WINDOW message will take care of redrawing the window, after
|
||||
// the device context has been recreated.
|
||||
self.draw_window(handle, true)
|
||||
} else {
|
||||
// Other device change messages are not handled.
|
||||
None
|
||||
}
|
||||
fn handle_device_lost(&self, lparam: LPARAM) -> Option<isize> {
|
||||
let mut lock = self.state.borrow_mut();
|
||||
let devices = lparam.0 as *const DirectXDevices;
|
||||
let devices = unsafe { &*devices };
|
||||
lock.renderer.handle_device_lost(&devices);
|
||||
Some(0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
ffi::OsStr,
|
||||
mem::ManuallyDrop,
|
||||
path::{Path, PathBuf},
|
||||
rc::{Rc, Weak},
|
||||
sync::Arc,
|
||||
@@ -17,7 +18,7 @@ use windows::{
|
||||
UI::ViewManagement::UISettings,
|
||||
Win32::{
|
||||
Foundation::*,
|
||||
Graphics::Gdi::*,
|
||||
Graphics::{Direct3D11::ID3D11Device, Gdi::*},
|
||||
Security::Credentials::*,
|
||||
System::{Com::*, LibraryLoader::*, Ole::*, SystemInformation::*},
|
||||
UI::{Input::KeyboardAndMouse::*, Shell::*, WindowsAndMessaging::*},
|
||||
@@ -55,6 +56,7 @@ pub(crate) struct WindowsPlatformState {
|
||||
jump_list: JumpList,
|
||||
// NOTE: standard cursor handles don't need to close.
|
||||
pub(crate) current_cursor: Option<HCURSOR>,
|
||||
directx_devices: ManuallyDrop<DirectXDevices>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -69,15 +71,17 @@ struct PlatformCallbacks {
|
||||
}
|
||||
|
||||
impl WindowsPlatformState {
|
||||
fn new() -> Self {
|
||||
fn new(directx_devices: DirectXDevices) -> Self {
|
||||
let callbacks = PlatformCallbacks::default();
|
||||
let jump_list = JumpList::new();
|
||||
let current_cursor = load_cursor(CursorStyle::Arrow);
|
||||
let directx_devices = ManuallyDrop::new(directx_devices);
|
||||
|
||||
Self {
|
||||
callbacks,
|
||||
jump_list,
|
||||
current_cursor,
|
||||
directx_devices,
|
||||
menus: Vec::new(),
|
||||
}
|
||||
}
|
||||
@@ -88,15 +92,21 @@ impl WindowsPlatform {
|
||||
unsafe {
|
||||
OleInitialize(None).context("unable to initialize Windows OLE")?;
|
||||
}
|
||||
let directx_devices = DirectXDevices::new().context("Creating DirectX devices")?;
|
||||
let (main_sender, main_receiver) = flume::unbounded::<Runnable>();
|
||||
let validation_number = rand::random::<usize>();
|
||||
let raw_window_handles = Arc::new(RwLock::new(SmallVec::new()));
|
||||
let text_system = Arc::new(
|
||||
DirectWriteTextSystem::new(&directx_devices)
|
||||
.context("Error creating DirectWriteTextSystem")?,
|
||||
);
|
||||
register_platform_window_class();
|
||||
let mut context = PlatformWindowCreateContext {
|
||||
inner: None,
|
||||
raw_window_handles: Arc::downgrade(&raw_window_handles),
|
||||
validation_number,
|
||||
main_receiver: Some(main_receiver),
|
||||
directx_devices: Some(directx_devices),
|
||||
};
|
||||
let result = unsafe {
|
||||
CreateWindowExW(
|
||||
@@ -125,12 +135,7 @@ impl WindowsPlatform {
|
||||
.is_ok_and(|value| value == "true" || value == "1");
|
||||
let background_executor = BackgroundExecutor::new(dispatcher.clone());
|
||||
let foreground_executor = ForegroundExecutor::new(dispatcher);
|
||||
let directx_devices = DirectXDevices::new(disable_direct_composition)
|
||||
.context("Unable to init directx devices.")?;
|
||||
let text_system = Arc::new(
|
||||
DirectWriteTextSystem::new(&directx_devices)
|
||||
.context("Error creating DirectWriteTextSystem")?,
|
||||
);
|
||||
|
||||
let drop_target_helper: IDropTargetHelper = unsafe {
|
||||
CoCreateInstance(&CLSID_DragDropHelper, None, CLSCTX_INPROC_SERVER)
|
||||
.context("Error creating drop target helper.")?
|
||||
@@ -181,6 +186,7 @@ impl WindowsPlatform {
|
||||
main_receiver: self.inner.main_receiver.clone(),
|
||||
platform_window_handle: self.handle,
|
||||
disable_direct_composition: self.disable_direct_composition,
|
||||
directx_devices: (*self.inner.state.borrow().directx_devices).clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,11 +234,24 @@ impl WindowsPlatform {
|
||||
}
|
||||
|
||||
fn begin_vsync_thread(&self) {
|
||||
let mut directx_device = (*self.inner.state.borrow().directx_devices).clone();
|
||||
let platform_window: SafeHwnd = self.handle.into();
|
||||
let validation_number = self.inner.validation_number;
|
||||
let all_windows = Arc::downgrade(&self.raw_window_handles);
|
||||
let text_system = Arc::downgrade(&self.text_system);
|
||||
std::thread::spawn(move || {
|
||||
let vsync_provider = VSyncProvider::new();
|
||||
loop {
|
||||
vsync_provider.wait_for_vsync();
|
||||
if check_device_lost(&directx_device.device) {
|
||||
handle_gpu_device_lost(
|
||||
&mut directx_device,
|
||||
platform_window.as_raw(),
|
||||
validation_number,
|
||||
&all_windows,
|
||||
&text_system,
|
||||
);
|
||||
}
|
||||
let Some(all_windows) = all_windows.upgrade() else {
|
||||
break;
|
||||
};
|
||||
@@ -647,7 +666,9 @@ impl Platform for WindowsPlatform {
|
||||
|
||||
impl WindowsPlatformInner {
|
||||
fn new(context: &mut PlatformWindowCreateContext) -> Result<Rc<Self>> {
|
||||
let state = RefCell::new(WindowsPlatformState::new());
|
||||
let state = RefCell::new(WindowsPlatformState::new(
|
||||
context.directx_devices.take().unwrap(),
|
||||
));
|
||||
Ok(Rc::new(Self {
|
||||
state,
|
||||
raw_window_handles: context.raw_window_handles.clone(),
|
||||
@@ -667,7 +688,8 @@ impl WindowsPlatformInner {
|
||||
WM_GPUI_CLOSE_ONE_WINDOW
|
||||
| WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD
|
||||
| WM_GPUI_DOCK_MENU_ACTION
|
||||
| WM_GPUI_KEYBOARD_LAYOUT_CHANGED => self.handle_gpui_events(msg, wparam, lparam),
|
||||
| WM_GPUI_KEYBOARD_LAYOUT_CHANGED
|
||||
| WM_GPUI_GPU_DEVICE_LOST => self.handle_gpui_events(msg, wparam, lparam),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(result) = handled {
|
||||
@@ -692,6 +714,7 @@ impl WindowsPlatformInner {
|
||||
WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD => self.run_foreground_task(),
|
||||
WM_GPUI_DOCK_MENU_ACTION => self.handle_dock_action_event(lparam.0 as _),
|
||||
WM_GPUI_KEYBOARD_LAYOUT_CHANGED => self.handle_keyboard_layout_change(),
|
||||
WM_GPUI_GPU_DEVICE_LOST => self.handle_device_lost(lparam),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
@@ -749,6 +772,18 @@ impl WindowsPlatformInner {
|
||||
self.state.borrow_mut().callbacks.keyboard_layout_change = Some(callback);
|
||||
Some(0)
|
||||
}
|
||||
|
||||
fn handle_device_lost(&self, lparam: LPARAM) -> Option<isize> {
|
||||
let mut lock = self.state.borrow_mut();
|
||||
let directx_devices = lparam.0 as *const DirectXDevices;
|
||||
let directx_devices = unsafe { &*directx_devices };
|
||||
unsafe {
|
||||
ManuallyDrop::drop(&mut lock.directx_devices);
|
||||
}
|
||||
lock.directx_devices = ManuallyDrop::new(directx_devices.clone());
|
||||
|
||||
Some(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for WindowsPlatform {
|
||||
@@ -762,6 +797,14 @@ impl Drop for WindowsPlatform {
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for WindowsPlatformState {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
ManuallyDrop::drop(&mut self.directx_devices);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct WindowCreationInfo {
|
||||
pub(crate) icon: HICON,
|
||||
pub(crate) executor: ForegroundExecutor,
|
||||
@@ -772,6 +815,7 @@ pub(crate) struct WindowCreationInfo {
|
||||
pub(crate) main_receiver: flume::Receiver<Runnable>,
|
||||
pub(crate) platform_window_handle: HWND,
|
||||
pub(crate) disable_direct_composition: bool,
|
||||
pub(crate) directx_devices: DirectXDevices,
|
||||
}
|
||||
|
||||
struct PlatformWindowCreateContext {
|
||||
@@ -779,6 +823,7 @@ struct PlatformWindowCreateContext {
|
||||
raw_window_handles: std::sync::Weak<RwLock<SmallVec<[SafeHwnd; 4]>>>,
|
||||
validation_number: usize,
|
||||
main_receiver: Option<flume::Receiver<Runnable>>,
|
||||
directx_devices: Option<DirectXDevices>,
|
||||
}
|
||||
|
||||
fn open_target(target: impl AsRef<OsStr>) -> Result<()> {
|
||||
@@ -951,6 +996,80 @@ fn should_auto_hide_scrollbars() -> Result<bool> {
|
||||
Ok(ui_settings.AutoHideScrollBars()?)
|
||||
}
|
||||
|
||||
fn check_device_lost(device: &ID3D11Device) -> bool {
|
||||
let device_state = unsafe { device.GetDeviceRemovedReason() };
|
||||
match device_state {
|
||||
Ok(_) => false,
|
||||
Err(err) => {
|
||||
log::error!("DirectX device lost detected: {:?}", err);
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_gpu_device_lost(
|
||||
directx_devices: &mut DirectXDevices,
|
||||
platform_window: HWND,
|
||||
validation_number: usize,
|
||||
all_windows: &std::sync::Weak<RwLock<SmallVec<[SafeHwnd; 4]>>>,
|
||||
text_system: &std::sync::Weak<DirectWriteTextSystem>,
|
||||
) {
|
||||
// Here we wait a bit to ensure the the system has time to recover from the device lost state.
|
||||
// If we don't wait, the final drawing result will be blank.
|
||||
std::thread::sleep(std::time::Duration::from_millis(350));
|
||||
|
||||
try_to_recover_from_device_lost(
|
||||
|| {
|
||||
DirectXDevices::new()
|
||||
.context("Failed to recreate new DirectX devices after device lost")
|
||||
},
|
||||
|new_devices| *directx_devices = new_devices,
|
||||
|| {
|
||||
log::error!("Failed to recover DirectX devices after multiple attempts.");
|
||||
// Do something here?
|
||||
// At this point, the device loss is considered unrecoverable.
|
||||
// std::process::exit(1);
|
||||
},
|
||||
);
|
||||
log::info!("DirectX devices successfully recreated.");
|
||||
|
||||
unsafe {
|
||||
SendMessageW(
|
||||
platform_window,
|
||||
WM_GPUI_GPU_DEVICE_LOST,
|
||||
Some(WPARAM(validation_number)),
|
||||
Some(LPARAM(directx_devices as *const _ as _)),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(text_system) = text_system.upgrade() {
|
||||
text_system.handle_gpu_lost(&directx_devices);
|
||||
}
|
||||
if let Some(all_windows) = all_windows.upgrade() {
|
||||
for window in all_windows.read().iter() {
|
||||
unsafe {
|
||||
SendMessageW(
|
||||
window.as_raw(),
|
||||
WM_GPUI_GPU_DEVICE_LOST,
|
||||
Some(WPARAM(validation_number)),
|
||||
Some(LPARAM(directx_devices as *const _ as _)),
|
||||
);
|
||||
}
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_millis(200));
|
||||
for window in all_windows.read().iter() {
|
||||
unsafe {
|
||||
SendMessageW(
|
||||
window.as_raw(),
|
||||
WM_GPUI_FORCE_UPDATE_WINDOW,
|
||||
Some(WPARAM(validation_number)),
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const PLATFORM_WINDOW_CLASS_NAME: PCWSTR = w!("Zed::PlatformWindow");
|
||||
|
||||
fn register_platform_window_class() {
|
||||
|
||||
@@ -79,6 +79,7 @@ pub(crate) struct WindowsWindowInner {
|
||||
impl WindowsWindowState {
|
||||
fn new(
|
||||
hwnd: HWND,
|
||||
directx_devices: &DirectXDevices,
|
||||
window_params: &CREATESTRUCTW,
|
||||
current_cursor: Option<HCURSOR>,
|
||||
display: WindowsDisplay,
|
||||
@@ -104,7 +105,7 @@ impl WindowsWindowState {
|
||||
};
|
||||
let border_offset = WindowBorderOffset::default();
|
||||
let restore_from_minimized = None;
|
||||
let renderer = DirectXRenderer::new(hwnd, disable_direct_composition)
|
||||
let renderer = DirectXRenderer::new(hwnd, directx_devices, disable_direct_composition)
|
||||
.context("Creating DirectX renderer")?;
|
||||
let callbacks = Callbacks::default();
|
||||
let input_handler = None;
|
||||
@@ -205,9 +206,10 @@ impl WindowsWindowState {
|
||||
}
|
||||
|
||||
impl WindowsWindowInner {
|
||||
fn new(context: &WindowCreateContext, hwnd: HWND, cs: &CREATESTRUCTW) -> Result<Rc<Self>> {
|
||||
fn new(context: &mut WindowCreateContext, hwnd: HWND, cs: &CREATESTRUCTW) -> Result<Rc<Self>> {
|
||||
let state = RefCell::new(WindowsWindowState::new(
|
||||
hwnd,
|
||||
&context.directx_devices,
|
||||
cs,
|
||||
context.current_cursor,
|
||||
context.display,
|
||||
@@ -345,6 +347,7 @@ struct WindowCreateContext {
|
||||
platform_window_handle: HWND,
|
||||
appearance: WindowAppearance,
|
||||
disable_direct_composition: bool,
|
||||
directx_devices: DirectXDevices,
|
||||
}
|
||||
|
||||
impl WindowsWindow {
|
||||
@@ -363,6 +366,7 @@ impl WindowsWindow {
|
||||
main_receiver,
|
||||
platform_window_handle,
|
||||
disable_direct_composition,
|
||||
directx_devices,
|
||||
} = creation_info;
|
||||
register_window_class(icon);
|
||||
let hide_title_bar = params
|
||||
@@ -422,6 +426,7 @@ impl WindowsWindow {
|
||||
platform_window_handle,
|
||||
appearance,
|
||||
disable_direct_composition,
|
||||
directx_devices,
|
||||
};
|
||||
let creation_result = unsafe {
|
||||
CreateWindowExW(
|
||||
|
||||
Reference in New Issue
Block a user