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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

//! Synchronization on the GPU.
//!
//! Just like for CPU code, you have to ensure that buffers and images are not accessed mutably by
//! multiple GPU queues simultaneously and that they are not accessed mutably by the CPU and by the
//! GPU simultaneously.
//!
//! This safety is enforced at runtime by vulkano but it is not magic and you will require some
//! knowledge if you want to avoid errors.

#[allow(unused)]
pub(crate) use self::pipeline::{PipelineStageAccess, PipelineStageAccessFlags};
pub use self::{
    future::{now, GpuFuture},
    pipeline::{
        AccessFlags, BufferMemoryBarrier, DependencyFlags, DependencyInfo, ImageMemoryBarrier,
        MemoryBarrier, PipelineStage, PipelineStages, QueueFamilyOwnershipTransfer,
    },
};
use crate::{device::Queue, VulkanError};
use std::{
    error::Error,
    fmt::{Display, Formatter},
    sync::Arc,
};

pub mod event;
pub mod fence;
pub mod future;
mod pipeline;
pub mod semaphore;

/// Declares in which queue(s) a resource can be used.
///
/// When you create a buffer or an image, you have to tell the Vulkan library in which queue
/// families it will be used. The vulkano library requires you to tell in which queue family
/// the resource will be used, even for exclusive mode.
#[derive(Debug, Clone, PartialEq, Eq)]
// TODO: remove
pub enum SharingMode {
    /// The resource is used is only one queue family.
    Exclusive,
    /// The resource is used in multiple queue families. Can be slower than `Exclusive`.
    Concurrent(Vec<u32>), // TODO: Vec is too expensive here
}

impl<'a> From<&'a Arc<Queue>> for SharingMode {
    #[inline]
    fn from(_queue: &'a Arc<Queue>) -> SharingMode {
        SharingMode::Exclusive
    }
}

impl<'a> From<&'a [&'a Arc<Queue>]> for SharingMode {
    #[inline]
    fn from(queues: &'a [&'a Arc<Queue>]) -> SharingMode {
        SharingMode::Concurrent(
            queues
                .iter()
                .map(|queue| queue.queue_family_index())
                .collect(),
        )
    }
}

/// Declares in which queue(s) a resource can be used.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Sharing<I>
where
    I: IntoIterator<Item = u32>,
{
    /// The resource is used is only one queue family.
    Exclusive,
    /// The resource is used in multiple queue families. Can be slower than `Exclusive`.
    Concurrent(I),
}

/// How the memory of a resource is currently being accessed.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum CurrentAccess {
    /// The resource is currently being accessed exclusively by the CPU.
    CpuExclusive,

    /// The resource is currently being accessed exclusively by the GPU.
    /// The GPU can have multiple exclusive accesses, if they are separated by synchronization.
    ///
    /// `gpu_writes` must not be 0. If it's decremented to 0, switch to `Shared`.
    GpuExclusive { gpu_reads: usize, gpu_writes: usize },

    /// The resource is not currently being accessed, or is being accessed for reading only.
    Shared { cpu_reads: usize, gpu_reads: usize },
}

/// Error when attempting to read or write a resource from the host (CPU).
#[derive(Clone, Debug)]
pub enum HostAccessError {
    AccessConflict(AccessConflict),
    Invalidate(VulkanError),
    NotHostMapped,
    OutOfMappedRange,
}

impl Error for HostAccessError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match self {
            Self::AccessConflict(err) => Some(err),
            Self::Invalidate(err) => Some(err),
            _ => None,
        }
    }
}

impl Display for HostAccessError {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
        match self {
            Self::AccessConflict(_) => {
                write!(f, "the resource is already in use in a conflicting way")
            }
            HostAccessError::Invalidate(_) => write!(f, "invalidating the device memory failed"),
            HostAccessError::NotHostMapped => {
                write!(f, "the device memory is not current host-mapped")
            }
            HostAccessError::OutOfMappedRange => write!(
                f,
                "the requested range is not within the currently mapped range of device memory",
            ),
        }
    }
}

/// Conflict when attempting to access a resource.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum AccessConflict {
    /// The resource is already locked for reading by the host (CPU).
    HostRead,

    /// The resource is already locked for writing by the host (CPU).
    HostWrite,

    /// The resource is already locked for reading by the device (GPU).
    DeviceRead,

    /// The resource is already locked for writing by the device (GPU).
    DeviceWrite,
}

impl Error for AccessConflict {}

impl Display for AccessConflict {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
        match self {
            AccessConflict::HostRead => write!(
                f,
                "the resource is already locked for reading by the host (CPU)"
            ),
            AccessConflict::HostWrite => write!(
                f,
                "the resource is already locked for writing by the host (CPU)"
            ),
            AccessConflict::DeviceRead => write!(
                f,
                "the resource is already locked for reading by the device (GPU)"
            ),
            AccessConflict::DeviceWrite => write!(
                f,
                "the resource is already locked for writing by the device (GPU)"
            ),
        }
    }
}