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
//! Resources to be handled by the engine like textures, sounds and fonts.
//!
//! Panics the program in case the system is not capable of running the game engine.

use crate::prelude::*;

use anyhow::{Context, Result};
use core::panic;
use once_cell::sync::Lazy;
use parking_lot::Mutex;
use std::sync::Arc;
use vulkano::buffer::BufferContents;
use vulkano::descriptor_set::WriteDescriptorSet;
use vulkano::pipeline::cache::{PipelineCache, PipelineCacheCreateInfo};

mod loader;
pub(crate) mod vulkan;
pub(crate) use loader::Loader;
use vulkan::Vulkan;

pub mod textures;

pub mod data;
pub mod materials;
mod model;

#[cfg(feature = "audio")]
pub mod sounds;
pub use model::*;

pub(crate) static RESOURCES: Lazy<Resources> = Lazy::new(|| {
    let vulkan = Vulkan::init().unwrap_or_else(|e| panic!("{e}"));
    Resources::new(vulkan).unwrap_or_else(|e| panic!("{e}"))
});
#[cfg(feature = "labels")]
pub(crate) static LABELIFIER: Lazy<Mutex<Labelifier>> =
    Lazy::new(|| Mutex::new(Labelifier::new().unwrap_or_else(|e| panic!("{e}"))));

/// All the resources kept in the game engine like textures, fonts, sounds and models.
#[derive(Clone)]
pub(crate) struct Resources {
    pub vulkan: Vulkan,
    pub loader: Arc<Mutex<Loader>>,
    pub shapes: BasicShapes,
    #[cfg(feature = "audio")]
    pub audio_server: crossbeam::channel::Sender<AudioUpdate>,
}

impl Resources {
    pub(crate) fn new(vulkan: Vulkan) -> Result<Self> {
        let loader = Arc::new(Mutex::new(Loader::init(&vulkan).context(
            "Failed to create the graphics loading environment for the game engine.",
        )?));
        let shapes = BasicShapes::new(&loader)
            .context("Failed to load default shapes into the GPU memory.")?;
        #[cfg(feature = "audio")]
        let audio_server = sounds::audio_server();
        Ok(Self {
            vulkan,
            loader,
            shapes,
            #[cfg(feature = "audio")]
            audio_server,
        })
    }

    pub(crate) fn vulkan(&self) -> &Vulkan {
        &self.vulkan
    }
    pub(crate) fn loader(&self) -> &Arc<Mutex<Loader>> {
        &self.loader
    }
    pub(crate) fn shapes(&self) -> &BasicShapes {
        &self.shapes
    }
}

/// Merges a pipeline cache into the resources potentially making the creation of materials faster.
///
/// # Safety
///
/// Unsafe because vulkan blindly trusts that this data comes from the `get_pipeline_binary` function.
/// The program will panic if the data provided is not right.
///
/// The binary given to the function must be made with the same hardware and vulkan driver version.
pub unsafe fn load_pipeline_cache(data: &[u8]) -> Result<()> {
    let cache = PipelineCache::new(
        RESOURCES.vulkan().device.clone(),
        PipelineCacheCreateInfo {
            initial_data: data.to_vec(),
            ..Default::default()
        },
    )?;
    RESOURCES
        .loader()
        .lock()
        .pipeline_cache
        .merge([cache.as_ref()])?;
    Ok(())
}

/// Returns the binary of the pipeline cache.
///
/// Allows this binary to be loaded with the `load_pipeline_cache` function to make loading materials potentially faster.
pub fn pipeline_binary() -> Result<Vec<u8>> {
    Ok(RESOURCES.loader().lock().pipeline_cache.get_data()?)
}

/// Loads a new write operation for a shader.
pub fn new_descriptor_write<T: BufferContents>(buf: T, set: u32) -> Result<WriteDescriptorSet> {
    let loader = RESOURCES.loader().lock();
    loader.write_descriptor(buf, set)
}