1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 // Copyright 2018 Jacob Lifshay
5 use image::{ImageMultisampleCount, ImageProperties, SupportedTilings, Tiling};
9 use std::ops::{Deref, DerefMut};
10 use std::os::raw::c_char;
11 use std::ptr::null_mut;
12 use std::ptr::NonNull;
13 use swapchain::{SurfaceImplementation, SurfacePlatform, Swapchain};
17 pub struct XcbSwapchain {}
19 impl Swapchain for XcbSwapchain {}
21 struct ReplyObject<T>(NonNull<T>);
23 impl<T> ReplyObject<T> {
24 unsafe fn from(v: *mut T) -> Option<Self> {
25 NonNull::new(v).map(ReplyObject)
29 impl<T> Deref for ReplyObject<T> {
31 fn deref(&self) -> &T {
32 unsafe { self.0.as_ref() }
36 impl<T> DerefMut for ReplyObject<T> {
37 fn deref_mut(&mut self) -> &mut T {
38 unsafe { self.0.as_mut() }
42 impl<T> Drop for ReplyObject<T> {
45 libc::free(self.0.as_ptr() as *mut libc::c_void);
50 struct ServerObject<Id: 'static + Copy> {
52 connection: *mut xcb::ffi::xcb_connection_t,
53 free_fn: unsafe extern "C" fn(connection: *mut xcb::ffi::xcb_connection_t, id: Id)
54 -> xcb::ffi::xcb_void_cookie_t,
57 impl<Id: 'static + Copy> ServerObject<Id> {
64 impl<Id: 'static + Copy> Drop for ServerObject<Id> {
67 (self.free_fn)(self.connection, self.id);
72 type Gc = ServerObject<xcb::ffi::xcb_gcontext_t>;
75 id: xcb::ffi::xcb_gcontext_t,
76 connection: *mut xcb::ffi::xcb_connection_t,
81 free_fn: xcb::ffi::xcb_free_gc,
85 type Pixmap = ServerObject<xcb::ffi::xcb_pixmap_t>;
88 unsafe fn create_pixmap(
89 id: xcb::ffi::xcb_pixmap_t,
90 connection: *mut xcb::ffi::xcb_connection_t,
95 free_fn: xcb::ffi::xcb_free_pixmap,
99 type ShmSeg = ServerObject<xcb::ffi::shm::xcb_shm_seg_t>;
102 unsafe fn create_shm_seg(
103 id: xcb::ffi::shm::xcb_shm_seg_t,
104 connection: *mut xcb::ffi::xcb_connection_t,
109 free_fn: xcb::ffi::shm::xcb_shm_detach,
113 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
114 enum SurfaceFormatGroup {
119 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
120 enum SwapchainSetupError {
125 unsafe fn query_extension(
126 connection: *mut xcb::ffi::xcb_connection_t,
127 extension_name: &str,
128 ) -> xcb::ffi::xcb_query_extension_cookie_t {
129 let len = extension_name.len() as u16;
130 assert_eq!(len as usize, extension_name.len());
131 xcb::ffi::xcb_query_extension(connection, len, extension_name.as_ptr() as *const c_char)
134 pub const MAX_SWAPCHAIN_IMAGE_COUNT: u32 = 16;
137 struct SwapchainSetupFirstStage {
141 surface_format_group: SurfaceFormatGroup,
142 present_modes: &'static [api::VkPresentModeKHR],
143 capabilities: api::VkSurfaceCapabilitiesKHR,
144 shared_present_capabilities: Option<api::VkSharedPresentSurfaceCapabilitiesKHR>,
145 image_pixel_size: usize,
146 scanline_alignment: usize,
147 shm_version: Option<xcb::ffi::shm::xcb_shm_query_version_cookie_t>,
148 image_properties: ImageProperties,
151 impl SwapchainSetupFirstStage {
153 connection: *mut xcb::ffi::xcb_connection_t,
154 window: xcb::ffi::xcb_window_t,
156 ) -> Result<Self, SwapchainSetupError> {
157 #![cfg_attr(feature = "cargo-clippy", allow(clippy::cast_lossless))]
158 let has_mit_shm = query_extension(connection, "MIT-SHM");
159 let geometry = xcb::ffi::xcb_get_geometry(connection, window);
160 let window_attributes = xcb::ffi::xcb_get_window_attributes(connection, window);
161 let tree = xcb::ffi::xcb_query_tree(connection, window);
162 let gc = xcb::ffi::xcb_generate_id(connection);
163 xcb::ffi::xcb_create_gc(
167 xcb::ffi::XCB_GC_GRAPHICS_EXPOSURES,
170 let gc = create_gc(gc, connection);
171 let has_mit_shm = ReplyObject::from(xcb::ffi::xcb_query_extension_reply(
176 let shm_supported = has_mit_shm.map(|v| v.present != 0).unwrap_or(false);
177 let shm_version = if is_full_setup && shm_supported {
178 Some(xcb::ffi::shm::xcb_shm_query_version(connection))
182 let geometry = ReplyObject::from(xcb::ffi::xcb_get_geometry_reply(
187 .ok_or(SwapchainSetupError::BadSurface)?;
188 let image_extent = api::VkExtent2D {
189 width: geometry.width as u32,
190 height: geometry.height as u32,
193 let window_attributes = ReplyObject::from(xcb::ffi::xcb_get_window_attributes_reply(
198 .ok_or(SwapchainSetupError::BadSurface)?;
199 let window_visual_id = window_attributes.visual;
200 mem::drop(window_attributes);
201 let tree = ReplyObject::from(xcb::ffi::xcb_query_tree_reply(connection, tree, null_mut()))
202 .ok_or(SwapchainSetupError::BadSurface)?;
203 let root_window = tree.root;
205 let mut screen = None;
207 xcb::ffi::xcb_setup_roots_iterator(xcb::ffi::xcb_get_setup(connection));
208 while roots_iter.rem != 0 {
209 if (*roots_iter.data).root == root_window {
210 screen = Some(roots_iter.data);
213 xcb::ffi::xcb_screen_next(&mut roots_iter);
215 let screen = screen.ok_or(SwapchainSetupError::BadSurface)?;
216 let mut window_visual_type_and_depth = None;
217 let mut depth_iter = xcb::ffi::xcb_screen_allowed_depths_iterator(screen);
218 while depth_iter.rem != 0 {
219 let mut visual_iter = xcb::ffi::xcb_depth_visuals_iterator(depth_iter.data);
220 while visual_iter.rem != 0 {
221 if (*visual_iter.data).visual_id == window_visual_id {
222 window_visual_type_and_depth =
223 Some((visual_iter.data, (*depth_iter.data).depth));
226 xcb::ffi::xcb_visualtype_next(&mut visual_iter);
228 if window_visual_type_and_depth.is_some() {
231 xcb::ffi::xcb_depth_next(&mut depth_iter);
233 let (window_visual_type, window_depth) =
234 window_visual_type_and_depth.ok_or(SwapchainSetupError::BadSurface)?;
235 let window_visual_type = &*window_visual_type;
236 let red_mask = window_visual_type.red_mask;
237 let green_mask = window_visual_type.green_mask;
238 let blue_mask = window_visual_type.blue_mask;
239 let alpha_mask = match window_depth {
241 32 => !(red_mask | green_mask | blue_mask),
242 _ => return Err(SwapchainSetupError::NoSupport),
244 let mut window_pixmap_format = None;
245 let mut formats_iter =
246 xcb::ffi::xcb_setup_pixmap_formats_iterator(xcb::ffi::xcb_get_setup(connection));
247 while formats_iter.rem != 0 {
248 if (*formats_iter.data).depth == window_depth {
249 window_pixmap_format = Some(formats_iter.data);
252 xcb::ffi::xcb_format_next(&mut formats_iter);
254 let window_pixmap_format =
255 &*(window_pixmap_format.ok_or(SwapchainSetupError::BadSurface)?);
256 let image_pixel_size = match window_pixmap_format.bits_per_pixel {
259 _ => return Err(SwapchainSetupError::NoSupport),
261 fn u32_from_bytes(v: [u8; 4]) -> u32 {
262 unsafe { mem::transmute(v) }
264 let surface_format_group = match (
265 u32_from_bytes([0xFF, 0, 0, 0]),
266 u32_from_bytes([0, 0xFF, 0, 0]),
267 u32_from_bytes([0, 0, 0xFF, 0]),
268 u32_from_bytes([0, 0, 0, 0xFF]),
274 && (alpha_mask == 0 || a == alpha_mask) =>
276 SurfaceFormatGroup::R8G8B8A8
282 && (alpha_mask == 0 || a == alpha_mask) =>
284 SurfaceFormatGroup::B8G8R8A8
286 _ => return Err(SwapchainSetupError::NoSupport),
288 let scanline_alignment = match window_pixmap_format.scanline_pad {
292 _ => unreachable!("invalid pixmap format scanline_pad"),
294 const PRESENT_MODES: &[api::VkPresentModeKHR] = &[
295 api::VK_PRESENT_MODE_FIFO_KHR, // FIXME: properly implement FIFO present mode using X11 Present extension
296 api::VK_PRESENT_MODE_IMMEDIATE_KHR,
302 surface_format_group,
303 present_modes: PRESENT_MODES,
304 capabilities: api::VkSurfaceCapabilitiesKHR {
306 maxImageCount: MAX_SWAPCHAIN_IMAGE_COUNT,
307 currentExtent: image_extent,
308 minImageExtent: image_extent,
309 maxImageExtent: image_extent,
310 maxImageArrayLayers: 1,
311 supportedTransforms: api::VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
312 currentTransform: api::VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
313 supportedCompositeAlpha: api::VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
314 supportedUsageFlags: api::VK_IMAGE_USAGE_TRANSFER_SRC_BIT
315 | api::VK_IMAGE_USAGE_TRANSFER_DST_BIT
316 | api::VK_IMAGE_USAGE_SAMPLED_BIT
317 | api::VK_IMAGE_USAGE_STORAGE_BIT
318 | api::VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
319 | api::VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT,
321 shared_present_capabilities: None,
325 image_properties: ImageProperties {
326 supported_tilings: SupportedTilings::Any,
327 format: api::VK_FORMAT_UNDEFINED,
328 extents: api::VkExtent3D {
329 width: image_extent.width,
330 height: image_extent.height,
335 multisample_count: ImageMultisampleCount::Count1,
336 swapchain_present_tiling: Some(Tiling::Linear),
344 _create_info: &api::VkSwapchainCreateInfoKHR,
345 _device_group_create_info: Option<&api::VkDeviceGroupSwapchainCreateInfoKHR>,
346 ) -> Result<Self, api::VkResult> {
352 pub struct XcbSurfaceImplementation;
354 impl XcbSurfaceImplementation {
355 unsafe fn get_surface(&self, surface: api::VkSurfaceKHR) -> &api::VkIcdSurfaceXcb {
356 let surface = surface.get().unwrap().as_ptr();
357 assert_eq!((*surface).platform, api::VK_ICD_WSI_PLATFORM_XCB);
358 #[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
359 &*(surface as *const api::VkIcdSurfaceXcb)
363 impl SurfaceImplementation for XcbSurfaceImplementation {
364 fn get_platform(&self) -> SurfacePlatform {
365 SurfacePlatform::VK_ICD_WSI_PLATFORM_XCB
367 unsafe fn get_surface_formats(
369 surface: api::VkSurfaceKHR,
370 ) -> Result<Cow<'static, [api::VkSurfaceFormatKHR]>, api::VkResult> {
371 let surface = &self.get_surface(surface);
372 let first_stage = SwapchainSetupFirstStage::new(surface.connection, surface.window, false)
373 .map_err(|v| match v {
374 SwapchainSetupError::BadSurface | SwapchainSetupError::NoSupport => {
375 api::VK_ERROR_SURFACE_LOST_KHR
378 match first_stage.surface_format_group {
379 SurfaceFormatGroup::B8G8R8A8 => {
380 const SURFACE_FORMATS: &[api::VkSurfaceFormatKHR] = &[
381 api::VkSurfaceFormatKHR {
382 format: api::VK_FORMAT_B8G8R8A8_SRGB,
383 colorSpace: api::VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
385 api::VkSurfaceFormatKHR {
386 format: api::VK_FORMAT_B8G8R8A8_UNORM,
387 colorSpace: api::VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
390 Ok(Cow::Borrowed(SURFACE_FORMATS))
392 SurfaceFormatGroup::R8G8B8A8 => {
393 const SURFACE_FORMATS: &[api::VkSurfaceFormatKHR] = &[
394 api::VkSurfaceFormatKHR {
395 format: api::VK_FORMAT_R8G8B8A8_SRGB,
396 colorSpace: api::VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
398 api::VkSurfaceFormatKHR {
399 format: api::VK_FORMAT_R8G8B8A8_UNORM,
400 colorSpace: api::VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
403 Ok(Cow::Borrowed(SURFACE_FORMATS))
407 unsafe fn get_present_modes(
409 surface: api::VkSurfaceKHR,
410 ) -> Result<Cow<'static, [api::VkPresentModeKHR]>, api::VkResult> {
411 let surface = &self.get_surface(surface);
412 let first_stage = SwapchainSetupFirstStage::new(surface.connection, surface.window, false)
413 .map_err(|v| match v {
414 SwapchainSetupError::BadSurface | SwapchainSetupError::NoSupport => {
415 api::VK_ERROR_SURFACE_LOST_KHR
418 Ok(Cow::Borrowed(first_stage.present_modes))
420 unsafe fn get_capabilities(
422 surface: api::VkSurfaceKHR,
423 ) -> Result<api::VkSurfaceCapabilitiesKHR, api::VkResult> {
424 let surface = &self.get_surface(surface);
425 let first_stage = SwapchainSetupFirstStage::new(surface.connection, surface.window, false)
426 .map_err(|v| match v {
427 SwapchainSetupError::BadSurface | SwapchainSetupError::NoSupport => {
428 api::VK_ERROR_SURFACE_LOST_KHR
431 Ok(first_stage.capabilities)
435 create_info: &api::VkSwapchainCreateInfoKHR,
436 device_group_create_info: Option<&api::VkDeviceGroupSwapchainCreateInfoKHR>,
437 ) -> Result<Box<Swapchain>, api::VkResult> {
438 Ok(Box::new(XcbSwapchain::new(
440 device_group_create_info,
443 unsafe fn destroy_surface(&self, surface: NonNull<api::VkIcdSurfaceBase>) {
444 #[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
445 Box::from_raw(surface.as_ptr() as *mut api::VkIcdSurfaceXcb);
447 fn duplicate(&self) -> Box<dyn SurfaceImplementation> {