aboutsummaryrefslogtreecommitdiff
path: root/stockton-skeleton/src/target.rs
blob: 23f56b95ee548365d8b914acc0699314a2880321 (plain)
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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
//! Resources needed for drawing on the screen, including sync objects
//! You likely won't need to interact with this directly

use crate::{
    context::ContextProperties,
    draw_passes::{DrawPass, Singular},
    types::*,
    session::Session
};

use std::{
    borrow::Borrow,
    iter::{empty, once},
    mem::ManuallyDrop,
};

use hal::{
    command::CommandBufferFlags,
    image::Usage as ImgUsage,
    window::{Extent2D, SwapchainConfig},
};

use anyhow::{Context, Result};

/// Holds our swapchain and other resources for drawing each frame
pub struct TargetChain {
    /// Surface we're targeting
    surface: ManuallyDrop<SurfaceT>,

    /// Command buffers and sync objects used when drawing
    resources: Box<[(CommandBufferT, SyncObjects)]>,

    /// Last image index of the swapchain drawn to
    last_resources: usize,
}

impl TargetChain {
    pub fn new(
        device: &mut DeviceT,
        mut surface: SurfaceT,
        cmd_pool: &mut CommandPoolT,
        properties: &ContextProperties,
    ) -> Result<TargetChain> {
        // Create swapchain
        let swap_config = SwapchainConfig {
            present_mode: properties.present_mode,
            composite_alpha_mode: properties.composite_alpha_mode,
            format: properties.color_format,
            extent: Extent2D {
                width: properties.extent.width,
                height: properties.extent.height,
            },
            image_count: properties.image_count,
            image_layers: 1,
            image_usage: ImgUsage::COLOR_ATTACHMENT,
        };

        // Create command buffers and sync objects
        let mut resources = Vec::with_capacity(swap_config.image_count as usize);

        for _ in 0..swap_config.image_count {
            resources.push((
                unsafe { cmd_pool.allocate_one(hal::command::Level::Primary) },
                SyncObjects::new(device).context("Error creating sync objects")?,
            ));
        }

        // Configure Swapchain
        unsafe {
            surface
                .configure_swapchain(device, swap_config)
                .context("Error configuring swapchain")?;
        }

        Ok(TargetChain {
            surface: ManuallyDrop::new(surface),
            resources: resources.into_boxed_slice(),
            last_resources: (properties.image_count - 1) as usize, // This means the next one to be used is index 0
        })
    }

    pub fn deactivate(
        self,
        instance: &mut InstanceT,
        device: &mut DeviceT,
        cmd_pool: &mut CommandPoolT,
    ) {
        let surface = self.deactivate_with_recyling(device, cmd_pool);

        unsafe {
            instance.destroy_surface(surface);
        }
    }

    pub fn deactivate_with_recyling(
        mut self,
        device: &mut DeviceT,
        cmd_pool: &mut CommandPoolT,
    ) -> SurfaceT {
        use core::ptr::read;
        unsafe {
            for i in 0..self.resources.len() {
                let (cmd_buf, syncs) = read(&self.resources[i]);
                cmd_pool.free(once(cmd_buf));
                syncs.deactivate(device);
            }

            self.surface.unconfigure_swapchain(device);
        }

        unsafe { ManuallyDrop::into_inner(read(&self.surface)) }
    }

    pub fn do_draw_with<'a, DP: DrawPass<Singular>>(
        &'a mut self,
        device: &mut DeviceT,
        command_queue: &mut QueueT,
        dp: &mut DP,
        session: &Session,
    ) -> Result<()> {
        self.last_resources = (self.last_resources + 1) % self.resources.len();

        let (cmd_buffer, syncs) = &mut self.resources[self.last_resources];

        // Get the image
        let (img, _) = unsafe {
            self.surface
                .acquire_image(core::u64::MAX)
                .context("Error getting image from swapchain")?
        };

        // Make sure whatever was last using this has finished
        unsafe {
            device
                .wait_for_fence(&syncs.present_complete, core::u64::MAX)
                .context("Error waiting for present_complete")?;
            device
                .reset_fence(&mut syncs.present_complete)
                .context("Error resetting present_complete fence")?;
        };

        // Record commands
        unsafe {
            cmd_buffer.begin_primary(CommandBufferFlags::empty());

            dp.queue_draw(session, img.borrow(), cmd_buffer)
                .context("Error in draw pass")?;

            cmd_buffer.finish();
        }

        // Submit it
        unsafe {
            command_queue.submit(
                once(&*cmd_buffer),
                empty(),
                once(&*syncs.render_complete),
                Some(&mut syncs.present_complete),
            );
            command_queue
                .present(&mut self.surface, img, Some(&mut *syncs.render_complete))
                .context("Error presenting to surface")?;
        };

        Ok(())
    }
}

pub struct SyncObjects {
    /// Triggered when rendering is done
    pub render_complete: ManuallyDrop<SemaphoreT>,

    /// Triggered when the image is on screen
    pub present_complete: ManuallyDrop<FenceT>,
}

impl SyncObjects {
    pub fn new(device: &mut DeviceT) -> Result<Self> {
        // Sync objects
        let render_complete = device
            .create_semaphore()
            .context("Error creating render_complete semaphore")?;
        let present_complete = device
            .create_fence(true)
            .context("Error creating present_complete fence")?;

        Ok(SyncObjects {
            render_complete: ManuallyDrop::new(render_complete),
            present_complete: ManuallyDrop::new(present_complete),
        })
    }

    pub fn deactivate(self, device: &mut DeviceT) {
        use core::ptr::read;

        unsafe {
            device.destroy_semaphore(ManuallyDrop::into_inner(read(&self.render_complete)));
            device.destroy_fence(ManuallyDrop::into_inner(read(&self.present_complete)));
        }
    }
}