Desktop: Isolate CEF-rendered UI into separate crate and process (#4321)

* Extract CEF rendered UI into a separate process and crate

* Review

* Review

* Review

* Review

* Remove necessary workarounds

* Block on frame copy ack

* Crop and resample frames correctly

* Skip blank frames

* Fix deps

* Fix fmt

* Fix clippy warning

* Review

* Fix todo
This commit is contained in:
Timon
2026-07-14 14:51:20 -07:00
committed by Keavon Chambers
parent 97a43e66fb
commit ba0a97aefe
82 changed files with 4768 additions and 1291 deletions
+136
View File
@@ -0,0 +1,136 @@
use super::{TextureImportError, TextureImportResult, TextureImporter, texture_descriptor, wgpu_format};
use cef::sys::cef_color_type_t;
use std::os::raw::c_void;
use wgpu::hal::api;
pub struct D3D11Importer {
pub handle: *mut c_void,
pub format: cef_color_type_t,
pub width: u32,
pub height: u32,
}
impl TextureImporter for D3D11Importer {
fn import_to_wgpu(&self, device: &wgpu::Device) -> TextureImportResult {
if self.handle.is_null() {
return Err(TextureImportError::InvalidHandle("Null D3D11 shared texture handle".to_string()));
}
let is_d3d12_backend = unsafe { device.as_hal::<api::Dx12>().is_some() };
if is_d3d12_backend {
let texture = self.import_via_d3d12(device)?;
return Ok(texture);
}
let texture = self.import_via_vulkan(device)?;
tracing::trace!("Successfully imported D3D11 shared texture via Vulkan");
Ok(texture)
}
}
impl D3D11Importer {
pub fn from_parts(handle: u64, width: u32, height: u32, format: cef_color_type_t) -> Self {
Self {
handle: handle as *mut c_void,
format,
width,
height,
}
}
fn import_via_d3d12(&self, device: &wgpu::Device) -> TextureImportResult {
use wgpu::hal::api;
let hal_texture = unsafe {
let hal_device_guard = device.as_hal::<api::Dx12>();
let Some(hal_device) = hal_device_guard else {
return Err(TextureImportError::HardwareUnavailable {
reason: "Device is not using D3D12 backend".to_string(),
});
};
let d3d12_resource = self.import_d3d11_handle_to_d3d12(&hal_device)?;
let hal_texture = <api::Dx12 as wgpu::hal::Api>::Device::texture_from_raw(
d3d12_resource,
wgpu_format(self.format)?,
wgpu::TextureDimension::D2,
wgpu::Extent3d {
width: self.width,
height: self.height,
depth_or_array_layers: 1,
},
1, // mip_level_count
1, // sample_count
);
Ok::<_, TextureImportError>(hal_texture)
}?;
let texture = unsafe { device.create_texture_from_hal::<api::Dx12>(hal_texture, &texture_descriptor(self.width, self.height, self.format, "CEF D3D11→D3D12 Shared Texture")?) };
Ok(texture)
}
fn import_via_vulkan(&self, device: &wgpu::Device) -> TextureImportResult {
use wgpu::{TextureUses, wgc::api::Vulkan};
let hal_texture = unsafe {
let hal_device_guard = device.as_hal::<Vulkan>();
let Some(hal_device) = hal_device_guard else {
return Err(TextureImportError::HardwareUnavailable {
reason: "Device is not using Vulkan backend".to_string(),
});
};
let hal_texture = <Vulkan as wgpu::hal::Api>::Device::texture_from_d3d11_shared_handle(
&hal_device,
windows::Win32::Foundation::HANDLE(self.handle),
&wgpu::hal::TextureDescriptor {
label: Some("CEF D3D11 Shared Texture"),
size: wgpu::Extent3d {
width: self.width,
height: self.height,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu_format(self.format)?,
usage: TextureUses::COPY_DST | TextureUses::COPY_SRC | TextureUses::RESOURCE,
memory_flags: wgpu::hal::MemoryFlags::empty(),
view_formats: vec![],
},
)
.map_err(|e| TextureImportError::PlatformError {
message: format!("Failed to import D3D11 shared handle into Vulkan: {:?}", e),
})?;
Ok::<_, TextureImportError>(hal_texture)
}?;
let texture = unsafe { device.create_texture_from_hal::<Vulkan>(hal_texture, &texture_descriptor(self.width, self.height, self.format, "CEF D3D11 Shared Texture")?) };
Ok(texture)
}
fn import_d3d11_handle_to_d3d12(&self, hal_device: &<wgpu::hal::api::Dx12 as wgpu::hal::Api>::Device) -> Result<windows::Win32::Graphics::Direct3D12::ID3D12Resource, TextureImportError> {
use windows::Win32::Graphics::Direct3D12::*;
let d3d12_device = hal_device.raw_device();
if self.width == 0 || self.height == 0 {
return Err(TextureImportError::InvalidHandle("Invalid D3D11 texture dimensions".to_string()));
}
unsafe {
let mut shared_resource: Option<ID3D12Resource> = None;
d3d12_device
.OpenSharedHandle(windows::Win32::Foundation::HANDLE(self.handle), &mut shared_resource)
.map_err(|e| TextureImportError::PlatformError {
message: format!("Failed to open D3D11 shared handle on D3D12: {:?}", e),
})?;
shared_resource.ok_or_else(|| TextureImportError::InvalidHandle("Failed to get D3D12 resource from shared handle".to_string()))
}
}
}
+226
View File
@@ -0,0 +1,226 @@
use super::{TextureImportError, TextureImportResult, TextureImporter, texture_descriptor, wgpu_format};
use ash::vk;
use cef::sys::cef_color_type_t;
use wgpu::hal::api;
pub struct DmaBufImporter {
fds: Vec<std::os::fd::OwnedFd>,
format: cef_color_type_t,
modifier: u64,
width: u32,
height: u32,
strides: Vec<u32>,
offsets: Vec<u32>,
}
impl TextureImporter for DmaBufImporter {
fn import_to_wgpu(&self, device: &wgpu::Device) -> TextureImportResult {
if self.fds.len() != 1 {
return Err(TextureImportError::InvalidHandle(format!("Expected exactly one DMA-BUF plane fd, got {}", self.fds.len())));
}
if self.strides.len() != self.fds.len() || self.offsets.len() != self.fds.len() {
return Err(TextureImportError::InvalidHandle(format!(
"DMA-BUF plane count mismatch: {} fds, {} strides, {} offsets",
self.fds.len(),
self.strides.len(),
self.offsets.len()
)));
}
let texture = self.import_via_vulkan(device)?;
tracing::trace!("Successfully imported DMA-BUF texture via Vulkan");
Ok(texture)
}
}
impl DmaBufImporter {
pub fn from_parts(fds: Vec<std::os::fd::OwnedFd>, strides: Vec<u32>, offsets: Vec<u32>, modifier: u64, width: u32, height: u32, format: cef_color_type_t) -> Self {
Self {
fds,
format,
modifier,
width,
height,
strides,
offsets,
}
}
fn import_via_vulkan(&self, device: &wgpu::Device) -> TextureImportResult {
use wgpu::{TextureUses, wgc::api::Vulkan};
let hal_texture = unsafe {
let hal_device_guard = device.as_hal::<api::Vulkan>();
let Some(hal_device) = hal_device_guard else {
return Err(TextureImportError::HardwareUnavailable {
reason: "Device is not using Vulkan backend".to_string(),
});
};
let (vk_image, device_memory) = self.create_vulkan_image_from_dmabuf(&hal_device)?;
let hal_texture = <api::Vulkan as wgpu::hal::Api>::Device::texture_from_raw(
&hal_device,
vk_image,
&wgpu::hal::TextureDescriptor {
label: Some("CEF DMA-BUF Texture"),
size: wgpu::Extent3d {
width: self.width,
height: self.height,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu_format(self.format)?,
usage: TextureUses::COPY_DST | TextureUses::COPY_SRC | TextureUses::RESOURCE,
memory_flags: wgpu::hal::MemoryFlags::empty(),
view_formats: vec![],
},
None,
wgpu::hal::vulkan::TextureMemory::Dedicated(device_memory),
);
Ok::<_, TextureImportError>(hal_texture)
}?;
let texture = unsafe { device.create_texture_from_hal::<Vulkan>(hal_texture, &texture_descriptor(self.width, self.height, self.format, "CEF DMA-BUF Texture")?) };
Ok(texture)
}
fn create_vulkan_image_from_dmabuf(&self, hal_device: &<api::Vulkan as wgpu::hal::Api>::Device) -> Result<(vk::Image, vk::DeviceMemory), TextureImportError> {
let device = hal_device.raw_device();
let instance = hal_device.shared_instance().raw_instance();
if self.width == 0 || self.height == 0 {
return Err(TextureImportError::InvalidHandle("Invalid DMA-BUF dimensions".to_string()));
}
let image_create_info = vk::ImageCreateInfo::default()
.image_type(vk::ImageType::TYPE_2D)
.format(vulkan_format(self.format)?)
.extent(vk::Extent3D {
width: self.width,
height: self.height,
depth: 1,
})
.mip_levels(1)
.array_layers(1)
.samples(vk::SampleCountFlags::TYPE_1)
.tiling(vk::ImageTiling::DRM_FORMAT_MODIFIER_EXT)
.usage(vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::COLOR_ATTACHMENT | vk::ImageUsageFlags::TRANSFER_SRC)
.sharing_mode(vk::SharingMode::EXCLUSIVE);
let plane_layouts = self
.offsets
.iter()
.zip(&self.strides)
.map(|(&offset, &stride)| vk::SubresourceLayout {
offset: offset as u64,
size: 0, // Will be calculated by driver
row_pitch: stride as u64,
array_pitch: 0,
depth_pitch: 0,
})
.collect::<Vec<_>>();
let mut drm_format_modifier = vk::ImageDrmFormatModifierExplicitCreateInfoEXT::default()
.drm_format_modifier(self.modifier)
.plane_layouts(&plane_layouts);
let mut external_memory_info = vk::ExternalMemoryImageCreateInfo::default().handle_types(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT);
let image_create_info = image_create_info.push_next(&mut drm_format_modifier).push_next(&mut external_memory_info);
let image = unsafe {
device.create_image(&image_create_info, None).map_err(|e| TextureImportError::VulkanError {
operation: format!("Failed to create Vulkan image: {e:?}"),
})?
};
let memory_requirements = unsafe { device.get_image_memory_requirements(image) };
// Duplicate the file descriptor
let dup_fd = unsafe { libc::dup(std::os::fd::AsRawFd::as_raw_fd(&self.fds[0])) };
if dup_fd == -1 {
// SAFETY: the image was created above and never bound or returned.
unsafe { device.destroy_image(image, None) };
return Err(TextureImportError::PlatformError {
message: "Failed to duplicate DMA-BUF file descriptor".to_string(),
});
}
let external_memory_fd = ash::khr::external_memory_fd::Device::new(instance, device);
let mut fd_properties = vk::MemoryFdPropertiesKHR::default();
if let Err(e) = unsafe { external_memory_fd.get_memory_fd_properties(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT, dup_fd, &mut fd_properties) } {
// SAFETY: import failed and the fd is still ours, need to clean up the image and close the fd
unsafe {
device.destroy_image(image, None);
libc::close(dup_fd);
}
return Err(TextureImportError::VulkanError {
operation: format!("Failed to query DMA-BUF fd memory properties: {e:?}"),
});
}
let mut import_memory_fd = vk::ImportMemoryFdInfoKHR::default().handle_type(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT).fd(dup_fd);
let memory_properties = unsafe { instance.get_physical_device_memory_properties(hal_device.raw_physical_device()) };
let compatible_type_bits = memory_requirements.memory_type_bits & fd_properties.memory_type_bits;
let Some(memory_type_index) = find_memory_type_index(compatible_type_bits, vk::MemoryPropertyFlags::empty(), &memory_properties) else {
// SAFETY: import failed and the fd is still ours, need to clean up the image and close the fd
unsafe {
device.destroy_image(image, None);
libc::close(dup_fd);
}
return Err(TextureImportError::VulkanError {
operation: "Failed to find suitable memory type for DMA-BUF".to_string(),
});
};
let allocate_info = vk::MemoryAllocateInfo::default()
.allocation_size(memory_requirements.size)
.memory_type_index(memory_type_index)
.push_next(&mut import_memory_fd);
let device_memory = match unsafe { device.allocate_memory(&allocate_info, None) } {
Ok(memory) => memory,
Err(e) => {
// SAFETY: import failed and the fd is still ours, need to clean up the image and close the fd
unsafe {
device.destroy_image(image, None);
libc::close(dup_fd);
}
return Err(TextureImportError::VulkanError {
operation: format!("Failed to allocate memory for DMA-BUF: {e:?}"),
});
}
};
if let Err(e) = unsafe { device.bind_image_memory(image, device_memory, 0) } {
// SAFETY: import failed, need to clean up the image and free the memory
unsafe {
device.destroy_image(image, None);
device.free_memory(device_memory, None);
}
return Err(TextureImportError::VulkanError {
operation: format!("Failed to bind memory to image: {e:?}"),
});
}
Ok((image, device_memory))
}
}
fn vulkan_format(format: cef_color_type_t) -> Result<vk::Format, TextureImportError> {
match format {
cef_color_type_t::CEF_COLOR_TYPE_BGRA_8888 => Ok(vk::Format::B8G8R8A8_UNORM),
cef_color_type_t::CEF_COLOR_TYPE_RGBA_8888 => Ok(vk::Format::R8G8B8A8_UNORM),
_ => Err(TextureImportError::UnsupportedFormat { format }),
}
}
fn find_memory_type_index(type_filter: u32, properties: vk::MemoryPropertyFlags, mem_properties: &vk::PhysicalDeviceMemoryProperties) -> Option<u32> {
(0..mem_properties.memory_type_count).find(|&i| (type_filter & (1 << i)) != 0 && mem_properties.memory_types[i as usize].property_flags.contains(properties))
}
+93
View File
@@ -0,0 +1,93 @@
use super::{TextureImportError, TextureImportResult, TextureImporter, texture_descriptor};
use cef::sys::cef_color_type_t;
use objc2::rc::Retained;
use objc2_io_surface::IOSurfaceRef;
use objc2_metal::{MTLDevice, MTLPixelFormat, MTLStorageMode, MTLTextureDescriptor, MTLTextureType, MTLTextureUsage};
use wgpu::TextureDescriptor;
use std::os::raw::c_void;
pub struct IOSurfaceImporter {
pub handle: *mut c_void,
pub format: cef_color_type_t,
pub width: u32,
pub height: u32,
}
impl TextureImporter for IOSurfaceImporter {
fn import_to_wgpu(&self, device: &wgpu::Device) -> TextureImportResult {
let texture = self.import_via_metal(device)?;
tracing::trace!("Successfully imported IOSurface texture via Metal");
Ok(texture)
}
}
impl IOSurfaceImporter {
pub fn from_parts(handle: *mut c_void, width: u32, height: u32, format: cef_color_type_t) -> Self {
Self { handle, format, width, height }
}
fn get_metal_desc(&self, texture_desc: &TextureDescriptor) -> Result<Retained<MTLTextureDescriptor>, TextureImportError> {
if self.width == 0 || self.height == 0 {
return Err(TextureImportError::InvalidHandle("Invalid IOSurface texture dimensions".to_string()));
}
let metal_desc = MTLTextureDescriptor::new();
unsafe {
metal_desc.setWidth(texture_desc.size.width as _);
metal_desc.setHeight(texture_desc.size.height as _);
metal_desc.setArrayLength(texture_desc.array_layer_count() as _);
metal_desc.setMipmapLevelCount(texture_desc.mip_level_count as _);
metal_desc.setSampleCount(texture_desc.sample_count as _);
metal_desc.setTextureType(MTLTextureType::Type2D);
metal_desc.setPixelFormat(match texture_desc.format {
wgpu::TextureFormat::Rgba8Unorm => MTLPixelFormat::RGBA8Unorm,
wgpu::TextureFormat::Bgra8Unorm => MTLPixelFormat::BGRA8Unorm,
_ => unimplemented!(),
});
metal_desc.setUsage(MTLTextureUsage::ShaderRead);
metal_desc.setStorageMode(MTLStorageMode::Managed);
}
Ok(metal_desc)
}
fn import_via_metal(&self, device: &wgpu::Device) -> TextureImportResult {
let io_surface = std::ptr::NonNull::new(self.handle.cast::<IOSurfaceRef>()).ok_or(TextureImportError::InvalidHandle("Invalid IOSurface handle".to_string()))?;
let texture_desc = texture_descriptor(self.width, self.height, self.format, "Cef Texture")?;
let hal_tex = {
let metal_desc = self.get_metal_desc(&texture_desc)?;
let texture = unsafe {
let hal_device_guard = device.as_hal::<wgpu::wgc::api::Metal>();
let Some(hal_device) = hal_device_guard else {
return Err(TextureImportError::InvalidHandle("Failed to get Metal device from wgpu".to_string()));
};
let texture = hal_device
.raw_device()
.newTextureWithDescriptor_iosurface_plane(metal_desc.as_ref(), io_surface.as_ref(), 0)
.ok_or(TextureImportError::InvalidHandle("Invalid IOSurface handle".to_string()))?;
let hal_tex = <wgpu::wgc::api::Metal as wgpu::hal::Api>::Device::texture_from_raw(
texture,
texture_desc.format,
MTLTextureType::Type2D,
texture_desc.array_layer_count(),
texture_desc.mip_level_count,
wgpu::hal::CopyExtent {
width: texture_desc.size.width,
height: texture_desc.size.height,
depth: texture_desc.array_layer_count(),
},
);
Ok::<_, TextureImportError>(hal_tex)
}?;
texture
};
Ok(unsafe { device.create_texture_from_hal::<wgpu::wgc::api::Metal>(hal_tex, &texture_desc) })
}
}