1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
use super::internal::*;
use crate::{RafxApiDef, RafxResult, RafxValidationMode};
use ash::vk;
use raw_window_handle::HasRawWindowHandle;
use std::sync::Arc;

use crate::vulkan::{RafxDeviceContextVulkan, RafxDeviceContextVulkanInner};
use std::ffi::CString;

/// Determines the method of finding the vulkan loader
#[derive(Copy, Clone, Debug)]
pub enum VulkanLinkMethod {
    /// Link vulkan dynamically (recommended and default)
    Dynamic,

    /// Assume the vulkan loader is statically linked.. intended for platforms like iOS
    #[cfg(feature = "static-vulkan")]
    Static,
}

impl Default for VulkanLinkMethod {
    fn default() -> Self {
        VulkanLinkMethod::Dynamic
    }
}

/// Vulkan-specific configuration
#[derive(Default)]
pub struct RafxApiDefVulkan {
    /// Used as a hint for drivers for what is being run. There are no special requirements for
    /// this. It is not visible to end-users.
    pub app_name: Option<CString>,

    /// Defines whether to load vulkan dynamically or use a statically-linked implementation. A
    /// common case where static linking is useful is linking MoltenVK on iOS devices
    pub link_method: Option<VulkanLinkMethod>,
    // The OS-specific layers/extensions are already included. Debug layers/extension are included
    // if enable_validation is true
    //TODO: Additional instance layer names
    //TODO: Additional instance extension names
    //TODO: Additional device extension names
}

pub struct RafxApiVulkan {
    instance: VkInstance,
    device_context: Option<RafxDeviceContextVulkan>,
}

impl Drop for RafxApiVulkan {
    fn drop(&mut self) {
        self.destroy().unwrap();
    }
}

impl RafxApiVulkan {
    pub fn device_context(&self) -> &RafxDeviceContextVulkan {
        self.device_context.as_ref().unwrap()
    }

    pub fn vk_instance(&self) -> &ash::Instance {
        &self.instance.instance
    }

    pub fn new(
        window: &dyn HasRawWindowHandle,
        api_def: &RafxApiDef,
        vk_api_def: &RafxApiDefVulkan,
    ) -> RafxResult<Self> {
        let link_method = vk_api_def.link_method.clone().unwrap_or_default();
        let app_name = vk_api_def
            .app_name
            .clone()
            .unwrap_or_else(|| CString::new("Rafx Application").unwrap());

        let (require_validation_layers_present, validation_layer_debug_report_flags) =
            match api_def.validation_mode {
                RafxValidationMode::Disabled => (false, vk::DebugReportFlagsEXT::empty()),
                RafxValidationMode::EnabledIfAvailable => (false, vk::DebugReportFlagsEXT::all()),
                RafxValidationMode::Enabled => (true, vk::DebugReportFlagsEXT::all()),
            };

        log::info!("Link method for vulkan: {:?}", link_method);
        let entry = match link_method {
            VulkanLinkMethod::Dynamic => VkEntry::new_dynamic(),
            #[cfg(feature = "static-vulkan")]
            VulkanLinkMethod::Static => VkEntry::new_static(),
        }?;

        let instance = VkInstance::new(
            entry,
            window,
            &app_name,
            require_validation_layers_present,
            validation_layer_debug_report_flags,
        )?;

        let inner = Arc::new(RafxDeviceContextVulkanInner::new(&instance)?);
        let device_context = RafxDeviceContextVulkan::new(inner)?;

        Ok(RafxApiVulkan {
            instance,
            device_context: Some(device_context),
        })
    }

    pub(crate) fn destroy(&mut self) -> RafxResult<()> {
        if let Some(device_context) = self.device_context.take() {
            // Clear any internal caches that may hold references to the device
            let inner = device_context.inner.clone();
            inner.descriptor_heap.clear_pools(device_context.device());
            inner.resource_cache.clear_caches();

            #[cfg(debug_assertions)]
            #[cfg(feature = "track-device-contexts")]
            let _create_index = device_context.create_index;

            // Thsi should be the final device context
            std::mem::drop(device_context);

            let _strong_count = Arc::strong_count(&inner);
            match Arc::try_unwrap(inner) {
                Ok(inner) => std::mem::drop(inner),
                Err(_arc) => {
                    Err(format!(
                        "Could not destroy device, {} references to it exist",
                        _strong_count
                    ))?;

                    #[cfg(debug_assertions)]
                    #[cfg(feature = "track-device-contexts")]
                    {
                        let mut all_contexts = _arc.all_contexts.lock().unwrap();
                        all_contexts.remove(&_create_index);
                        for (k, v) in all_contexts.iter_mut() {
                            v.resolve();
                            println!("context allocation: {}\n{:?}", k, v);
                        }
                    }
                }
            }
        }

        Ok(())
    }
}