7aad1626f70b4ce62b5af30c892a3d1e8b0ec78a
[kazan.git] / vulkan-driver / src / xcb_swapchain.rs
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 // Copyright 2018 Jacob Lifshay
3 use api;
4 use handle::Handle;
5 use image::{ImageMultisampleCount, ImageProperties, SupportedTilings, Tiling};
6 use libc;
7 use std::borrow::Cow;
8 use std::mem;
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};
14 use xcb;
15
16 #[derive(Debug)]
17 pub struct XcbSwapchain {}
18
19 impl Swapchain for XcbSwapchain {}
20
21 struct ReplyObject<T>(NonNull<T>);
22
23 impl<T> ReplyObject<T> {
24 unsafe fn from(v: *mut T) -> Option<Self> {
25 NonNull::new(v).map(ReplyObject)
26 }
27 }
28
29 impl<T> Deref for ReplyObject<T> {
30 type Target = T;
31 fn deref(&self) -> &T {
32 unsafe { self.0.as_ref() }
33 }
34 }
35
36 impl<T> DerefMut for ReplyObject<T> {
37 fn deref_mut(&mut self) -> &mut T {
38 unsafe { self.0.as_mut() }
39 }
40 }
41
42 impl<T> Drop for ReplyObject<T> {
43 fn drop(&mut self) {
44 unsafe {
45 libc::free(self.0.as_ptr() as *mut libc::c_void);
46 }
47 }
48 }
49
50 struct ServerObject<Id: 'static + Copy> {
51 id: Id,
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,
55 }
56
57 impl<Id: 'static + Copy> ServerObject<Id> {
58 #[allow(dead_code)]
59 fn get(&self) -> Id {
60 self.id
61 }
62 }
63
64 impl<Id: 'static + Copy> Drop for ServerObject<Id> {
65 fn drop(&mut self) {
66 unsafe {
67 (self.free_fn)(self.connection, self.id);
68 }
69 }
70 }
71
72 type Gc = ServerObject<xcb::ffi::xcb_gcontext_t>;
73
74 unsafe fn create_gc(
75 id: xcb::ffi::xcb_gcontext_t,
76 connection: *mut xcb::ffi::xcb_connection_t,
77 ) -> Gc {
78 ServerObject {
79 id,
80 connection,
81 free_fn: xcb::ffi::xcb_free_gc,
82 }
83 }
84
85 type Pixmap = ServerObject<xcb::ffi::xcb_pixmap_t>;
86
87 #[allow(dead_code)]
88 unsafe fn create_pixmap(
89 id: xcb::ffi::xcb_pixmap_t,
90 connection: *mut xcb::ffi::xcb_connection_t,
91 ) -> Pixmap {
92 ServerObject {
93 id,
94 connection,
95 free_fn: xcb::ffi::xcb_free_pixmap,
96 }
97 }
98
99 type ShmSeg = ServerObject<xcb::ffi::shm::xcb_shm_seg_t>;
100
101 #[allow(dead_code)]
102 unsafe fn create_shm_seg(
103 id: xcb::ffi::shm::xcb_shm_seg_t,
104 connection: *mut xcb::ffi::xcb_connection_t,
105 ) -> ShmSeg {
106 ServerObject {
107 id,
108 connection,
109 free_fn: xcb::ffi::shm::xcb_shm_detach,
110 }
111 }
112
113 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
114 enum SurfaceFormatGroup {
115 R8G8B8A8,
116 B8G8R8A8,
117 }
118
119 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
120 enum SwapchainSetupError {
121 BadSurface,
122 NoSupport,
123 }
124
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)
132 }
133
134 pub const MAX_SWAPCHAIN_IMAGE_COUNT: u32 = 16;
135
136 #[allow(dead_code)]
137 struct SwapchainSetupFirstStage {
138 gc: Gc,
139 shm_supported: bool,
140 window_depth: u8,
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,
149 }
150
151 impl SwapchainSetupFirstStage {
152 unsafe fn new(
153 connection: *mut xcb::ffi::xcb_connection_t,
154 window: xcb::ffi::xcb_window_t,
155 is_full_setup: bool,
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(
164 connection,
165 gc,
166 window,
167 xcb::ffi::XCB_GC_GRAPHICS_EXPOSURES,
168 [0].as_ptr(),
169 );
170 let gc = create_gc(gc, connection);
171 let has_mit_shm = ReplyObject::from(xcb::ffi::xcb_query_extension_reply(
172 connection,
173 has_mit_shm,
174 null_mut(),
175 ));
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))
179 } else {
180 None
181 };
182 let geometry = ReplyObject::from(xcb::ffi::xcb_get_geometry_reply(
183 connection,
184 geometry,
185 null_mut(),
186 ))
187 .ok_or(SwapchainSetupError::BadSurface)?;
188 let image_extent = api::VkExtent2D {
189 width: geometry.width as u32,
190 height: geometry.height as u32,
191 };
192 mem::drop(geometry);
193 let window_attributes = ReplyObject::from(xcb::ffi::xcb_get_window_attributes_reply(
194 connection,
195 window_attributes,
196 null_mut(),
197 ))
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;
204 mem::drop(tree);
205 let mut screen = None;
206 let mut roots_iter =
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);
211 break;
212 }
213 xcb::ffi::xcb_screen_next(&mut roots_iter);
214 }
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));
224 break;
225 }
226 xcb::ffi::xcb_visualtype_next(&mut visual_iter);
227 }
228 if window_visual_type_and_depth.is_some() {
229 break;
230 }
231 xcb::ffi::xcb_depth_next(&mut depth_iter);
232 }
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 {
240 24 => 0,
241 32 => !(red_mask | green_mask | blue_mask),
242 _ => return Err(SwapchainSetupError::NoSupport),
243 };
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);
250 break;
251 }
252 xcb::ffi::xcb_format_next(&mut formats_iter);
253 }
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 {
257 24 => 3,
258 32 => 4,
259 _ => return Err(SwapchainSetupError::NoSupport),
260 };
261 fn u32_from_bytes(v: [u8; 4]) -> u32 {
262 unsafe { mem::transmute(v) }
263 }
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]),
269 ) {
270 (r, g, b, a)
271 if r == red_mask
272 && g == green_mask
273 && b == blue_mask
274 && (alpha_mask == 0 || a == alpha_mask) =>
275 {
276 SurfaceFormatGroup::R8G8B8A8
277 }
278 (b, g, r, a)
279 if r == red_mask
280 && g == green_mask
281 && b == blue_mask
282 && (alpha_mask == 0 || a == alpha_mask) =>
283 {
284 SurfaceFormatGroup::B8G8R8A8
285 }
286 _ => return Err(SwapchainSetupError::NoSupport),
287 };
288 let scanline_alignment = match window_pixmap_format.scanline_pad {
289 8 => 1,
290 16 => 2,
291 32 => 4,
292 _ => unreachable!("invalid pixmap format scanline_pad"),
293 };
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,
297 ];
298 Ok(Self {
299 gc,
300 shm_supported,
301 window_depth,
302 surface_format_group,
303 present_modes: PRESENT_MODES,
304 capabilities: api::VkSurfaceCapabilitiesKHR {
305 minImageCount: 2,
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,
320 },
321 shared_present_capabilities: None,
322 image_pixel_size,
323 scanline_alignment,
324 shm_version,
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,
331 depth: 1,
332 },
333 array_layers: 1,
334 mip_levels: 1,
335 multisample_count: ImageMultisampleCount::Count1,
336 swapchain_present_tiling: Some(Tiling::Linear),
337 },
338 })
339 }
340 }
341
342 impl XcbSwapchain {
343 unsafe fn new(
344 _create_info: &api::VkSwapchainCreateInfoKHR,
345 _device_group_create_info: Option<&api::VkDeviceGroupSwapchainCreateInfoKHR>,
346 ) -> Result<Self, api::VkResult> {
347 unimplemented!()
348 }
349 }
350
351 #[derive(Debug)]
352 pub struct XcbSurfaceImplementation;
353
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)
360 }
361 }
362
363 impl SurfaceImplementation for XcbSurfaceImplementation {
364 fn get_platform(&self) -> SurfacePlatform {
365 SurfacePlatform::VK_ICD_WSI_PLATFORM_XCB
366 }
367 unsafe fn get_surface_formats(
368 &self,
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
376 }
377 })?;
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,
384 },
385 api::VkSurfaceFormatKHR {
386 format: api::VK_FORMAT_B8G8R8A8_UNORM,
387 colorSpace: api::VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
388 },
389 ];
390 Ok(Cow::Borrowed(SURFACE_FORMATS))
391 }
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,
397 },
398 api::VkSurfaceFormatKHR {
399 format: api::VK_FORMAT_R8G8B8A8_UNORM,
400 colorSpace: api::VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
401 },
402 ];
403 Ok(Cow::Borrowed(SURFACE_FORMATS))
404 }
405 }
406 }
407 unsafe fn get_present_modes(
408 &self,
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
416 }
417 })?;
418 Ok(Cow::Borrowed(first_stage.present_modes))
419 }
420 unsafe fn get_capabilities(
421 &self,
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
429 }
430 })?;
431 Ok(first_stage.capabilities)
432 }
433 unsafe fn build(
434 &self,
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(
439 create_info,
440 device_group_create_info,
441 )?))
442 }
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);
446 }
447 fn duplicate(&self) -> Box<dyn SurfaceImplementation> {
448 Box::new(Self {})
449 }
450 }