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> {
63 impl<Id: 'static + Copy> Drop for ServerObject<Id> {
66 (self.free_fn)(self.connection, self.id);
71 type Gc = ServerObject<xcb::ffi::xcb_gcontext_t>;
74 id: xcb::ffi::xcb_gcontext_t,
75 connection: *mut xcb::ffi::xcb_connection_t,
80 free_fn: xcb::ffi::xcb_free_gc,
84 type Pixmap = ServerObject<xcb::ffi::xcb_pixmap_t>;
86 unsafe fn create_pixmap(
87 id: xcb::ffi::xcb_pixmap_t,
88 connection: *mut xcb::ffi::xcb_connection_t,
93 free_fn: xcb::ffi::xcb_free_pixmap,
97 type ShmSeg = ServerObject<xcb::ffi::shm::xcb_shm_seg_t>;
99 unsafe fn create_shm_seg(
100 id: xcb::ffi::shm::xcb_shm_seg_t,
101 connection: *mut xcb::ffi::xcb_connection_t,
106 free_fn: xcb::ffi::shm::xcb_shm_detach,
110 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
111 enum SurfaceFormatGroup {
116 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
117 enum SwapchainSetupError {
122 unsafe fn query_extension(
123 connection: *mut xcb::ffi::xcb_connection_t,
124 extension_name: &str,
125 ) -> xcb::ffi::xcb_query_extension_cookie_t {
126 let len = extension_name.len() as u16;
127 assert_eq!(len as usize, extension_name.len());
128 xcb::ffi::xcb_query_extension(connection, len, extension_name.as_ptr() as *const c_char)
131 pub const MAX_SWAPCHAIN_IMAGE_COUNT: u32 = 16;
133 struct SwapchainSetupFirstStage {
137 surface_format_group: SurfaceFormatGroup,
138 present_modes: &'static [api::VkPresentModeKHR],
139 capabilities: api::VkSurfaceCapabilitiesKHR,
140 shared_present_capabilities: Option<api::VkSharedPresentSurfaceCapabilitiesKHR>,
141 image_pixel_size: usize,
142 scanline_alignment: usize,
143 shm_version: Option<xcb::ffi::shm::xcb_shm_query_version_cookie_t>,
144 image_properties: ImageProperties,
147 impl SwapchainSetupFirstStage {
149 connection: *mut xcb::ffi::xcb_connection_t,
150 window: xcb::ffi::xcb_window_t,
152 ) -> Result<Self, SwapchainSetupError> {
153 let has_mit_shm = query_extension(connection, "MIT-SHM");
154 let geometry = xcb::ffi::xcb_get_geometry(connection, window);
155 let window_attributes = xcb::ffi::xcb_get_window_attributes(connection, window);
156 let tree = xcb::ffi::xcb_query_tree(connection, window);
157 let gc = xcb::ffi::xcb_generate_id(connection);
158 xcb::ffi::xcb_create_gc(
162 xcb::ffi::XCB_GC_GRAPHICS_EXPOSURES,
165 let gc = create_gc(gc, connection);
166 let has_mit_shm = ReplyObject::from(xcb::ffi::xcb_query_extension_reply(
171 let shm_supported = has_mit_shm.map(|v| v.present != 0).unwrap_or(false);
172 let shm_version = if is_full_setup && shm_supported {
173 Some(xcb::ffi::shm::xcb_shm_query_version(connection))
177 let geometry = ReplyObject::from(xcb::ffi::xcb_get_geometry_reply(
182 .ok_or(SwapchainSetupError::BadSurface)?;
183 let image_extent = api::VkExtent2D {
184 width: geometry.width as u32,
185 height: geometry.height as u32,
188 let window_attributes = ReplyObject::from(xcb::ffi::xcb_get_window_attributes_reply(
193 .ok_or(SwapchainSetupError::BadSurface)?;
194 let window_visual_id = window_attributes.visual;
195 mem::drop(window_attributes);
196 let tree = ReplyObject::from(xcb::ffi::xcb_query_tree_reply(connection, tree, null_mut()))
197 .ok_or(SwapchainSetupError::BadSurface)?;
198 let root_window = tree.root;
200 let mut screen = None;
202 xcb::ffi::xcb_setup_roots_iterator(xcb::ffi::xcb_get_setup(connection));
203 while roots_iter.rem != 0 {
204 if (*roots_iter.data).root == root_window {
205 screen = Some(roots_iter.data);
208 xcb::ffi::xcb_screen_next(&mut roots_iter);
210 let screen = screen.ok_or(SwapchainSetupError::BadSurface)?;
211 let mut window_visual_type_and_depth = None;
212 let mut depth_iter = xcb::ffi::xcb_screen_allowed_depths_iterator(screen);
213 while depth_iter.rem != 0 {
214 let mut visual_iter = xcb::ffi::xcb_depth_visuals_iterator(depth_iter.data);
215 while visual_iter.rem != 0 {
216 if (*visual_iter.data).visual_id == window_visual_id {
217 window_visual_type_and_depth =
218 Some((visual_iter.data, (*depth_iter.data).depth));
221 xcb::ffi::xcb_visualtype_next(&mut visual_iter);
223 if window_visual_type_and_depth.is_some() {
226 xcb::ffi::xcb_depth_next(&mut depth_iter);
228 let (window_visual_type, window_depth) =
229 window_visual_type_and_depth.ok_or(SwapchainSetupError::BadSurface)?;
230 let ref window_visual_type = *window_visual_type;
231 let red_mask = window_visual_type.red_mask;
232 let green_mask = window_visual_type.green_mask;
233 let blue_mask = window_visual_type.blue_mask;
234 let alpha_mask = match window_depth {
236 32 => !(red_mask | green_mask | blue_mask),
237 _ => return Err(SwapchainSetupError::NoSupport),
239 let mut window_pixmap_format = None;
240 let mut formats_iter =
241 xcb::ffi::xcb_setup_pixmap_formats_iterator(xcb::ffi::xcb_get_setup(connection));
242 while formats_iter.rem != 0 {
243 if (*formats_iter.data).depth == window_depth {
244 window_pixmap_format = Some(formats_iter.data);
247 xcb::ffi::xcb_format_next(&mut formats_iter);
249 let ref window_pixmap_format =
250 *(window_pixmap_format.ok_or(SwapchainSetupError::BadSurface)?);
251 let image_pixel_size = match window_pixmap_format.bits_per_pixel {
254 _ => return Err(SwapchainSetupError::NoSupport),
256 fn u32_from_bytes(v: [u8; 4]) -> u32 {
257 unsafe { mem::transmute(v) }
259 let surface_format_group = match (
260 u32_from_bytes([0xFF, 0, 0, 0]),
261 u32_from_bytes([0, 0xFF, 0, 0]),
262 u32_from_bytes([0, 0, 0xFF, 0]),
263 u32_from_bytes([0, 0, 0, 0xFF]),
269 && (alpha_mask == 0 || a == alpha_mask) =>
271 SurfaceFormatGroup::R8G8B8A8
277 && (alpha_mask == 0 || a == alpha_mask) =>
279 SurfaceFormatGroup::B8G8R8A8
281 _ => return Err(SwapchainSetupError::NoSupport),
283 let scanline_alignment = match window_pixmap_format.scanline_pad {
287 _ => unreachable!("invalid pixmap format scanline_pad"),
289 const PRESENT_MODES: &'static [api::VkPresentModeKHR] = &[
290 api::VK_PRESENT_MODE_FIFO_KHR, // FIXME: properly implement FIFO present mode using X11 Present extension
291 api::VK_PRESENT_MODE_IMMEDIATE_KHR,
297 surface_format_group,
298 present_modes: PRESENT_MODES,
299 capabilities: api::VkSurfaceCapabilitiesKHR {
301 maxImageCount: MAX_SWAPCHAIN_IMAGE_COUNT,
302 currentExtent: image_extent,
303 minImageExtent: image_extent,
304 maxImageExtent: image_extent,
305 maxImageArrayLayers: 1,
306 supportedTransforms: api::VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
307 currentTransform: api::VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
308 supportedCompositeAlpha: api::VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
309 supportedUsageFlags: api::VK_IMAGE_USAGE_TRANSFER_SRC_BIT
310 | api::VK_IMAGE_USAGE_TRANSFER_DST_BIT
311 | api::VK_IMAGE_USAGE_SAMPLED_BIT
312 | api::VK_IMAGE_USAGE_STORAGE_BIT
313 | api::VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
314 | api::VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT,
316 shared_present_capabilities: None,
320 image_properties: ImageProperties {
321 supported_tilings: SupportedTilings::Any,
322 format: api::VK_FORMAT_UNDEFINED,
323 extents: api::VkExtent3D {
324 width: image_extent.width,
325 height: image_extent.height,
330 multisample_count: ImageMultisampleCount::Count1,
331 swapchain_present_tiling: Some(Tiling::Linear),
339 create_info: &api::VkSwapchainCreateInfoKHR,
340 device_group_create_info: Option<&api::VkDeviceGroupSwapchainCreateInfoKHR>,
341 ) -> Result<Self, api::VkResult> {
347 pub struct XcbSurfaceImplementation;
349 impl XcbSurfaceImplementation {
350 unsafe fn get_surface(&self, surface: api::VkSurfaceKHR) -> &api::VkIcdSurfaceXcb {
351 let surface = surface.get().unwrap().as_ptr();
352 assert_eq!((*surface).platform, api::VK_ICD_WSI_PLATFORM_XCB);
353 &*(surface as *const api::VkIcdSurfaceXcb)
357 impl SurfaceImplementation for XcbSurfaceImplementation {
358 fn get_platform(&self) -> SurfacePlatform {
359 SurfacePlatform::VK_ICD_WSI_PLATFORM_XCB
361 unsafe fn get_surface_formats(
363 surface: api::VkSurfaceKHR,
364 ) -> Result<Cow<'static, [api::VkSurfaceFormatKHR]>, api::VkResult> {
365 let surface = &self.get_surface(surface);
366 let first_stage = SwapchainSetupFirstStage::new(surface.connection, surface.window, false)
367 .map_err(|v| match v {
368 SwapchainSetupError::BadSurface | SwapchainSetupError::NoSupport => {
369 api::VK_ERROR_SURFACE_LOST_KHR
372 match first_stage.surface_format_group {
373 SurfaceFormatGroup::B8G8R8A8 => {
374 const SURFACE_FORMATS: &'static [api::VkSurfaceFormatKHR] = &[
375 api::VkSurfaceFormatKHR {
376 format: api::VK_FORMAT_B8G8R8A8_SRGB,
377 colorSpace: api::VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
379 api::VkSurfaceFormatKHR {
380 format: api::VK_FORMAT_B8G8R8A8_UNORM,
381 colorSpace: api::VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
384 Ok(Cow::Borrowed(SURFACE_FORMATS))
386 SurfaceFormatGroup::R8G8B8A8 => {
387 const SURFACE_FORMATS: &'static [api::VkSurfaceFormatKHR] = &[
388 api::VkSurfaceFormatKHR {
389 format: api::VK_FORMAT_R8G8B8A8_SRGB,
390 colorSpace: api::VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
392 api::VkSurfaceFormatKHR {
393 format: api::VK_FORMAT_R8G8B8A8_UNORM,
394 colorSpace: api::VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
397 Ok(Cow::Borrowed(SURFACE_FORMATS))
401 unsafe fn get_present_modes(
403 surface: api::VkSurfaceKHR,
404 ) -> Result<Cow<'static, [api::VkPresentModeKHR]>, api::VkResult> {
405 let surface = &self.get_surface(surface);
406 let first_stage = SwapchainSetupFirstStage::new(surface.connection, surface.window, false)
407 .map_err(|v| match v {
408 SwapchainSetupError::BadSurface | SwapchainSetupError::NoSupport => {
409 api::VK_ERROR_SURFACE_LOST_KHR
412 Ok(Cow::Borrowed(first_stage.present_modes))
414 unsafe fn get_capabilities(
416 surface: api::VkSurfaceKHR,
417 ) -> Result<api::VkSurfaceCapabilitiesKHR, api::VkResult> {
418 let surface = &self.get_surface(surface);
419 let first_stage = SwapchainSetupFirstStage::new(surface.connection, surface.window, false)
420 .map_err(|v| match v {
421 SwapchainSetupError::BadSurface | SwapchainSetupError::NoSupport => {
422 api::VK_ERROR_SURFACE_LOST_KHR
425 Ok(first_stage.capabilities)
429 create_info: &api::VkSwapchainCreateInfoKHR,
430 device_group_create_info: Option<&api::VkDeviceGroupSwapchainCreateInfoKHR>,
431 ) -> Result<Box<Swapchain>, api::VkResult> {
432 Ok(Box::new(XcbSwapchain::new(
434 device_group_create_info,
437 unsafe fn destroy_surface(&self, surface: NonNull<api::VkIcdSurfaceBase>) {
438 Box::from_raw(surface.as_ptr() as *mut api::VkIcdSurfaceXcb);
440 fn duplicate(&self) -> Box<dyn SurfaceImplementation> {