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
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
|
//! Resources needed for drawing on the screen, including sync objects
use std::{
borrow::Borrow,
iter::{empty, once},
mem::ManuallyDrop,
};
use hal::{
command::CommandBufferFlags,
format::{Aspects, ChannelType, Format, ImageFeature},
image::{Access, Extent, Layout, SubresourceRange, Usage as ImgUsage},
memory::{Barrier, Dependencies},
pso::{PipelineStage, Viewport},
window::{CompositeAlphaMode, Extent2D, PresentMode, SwapchainConfig},
};
use super::{draw_passes::DrawPass};
use crate::{error::EnvironmentError, types::*};
use anyhow::{Context, Result};
use stockton_types::Session;
#[derive(Debug, Clone)]
pub struct SwapchainProperties {
pub format: Format,
pub depth_format: Format,
pub present_mode: PresentMode,
pub composite_alpha_mode: CompositeAlphaMode,
pub viewport: Viewport,
pub extent: Extent,
pub image_count: u32,
}
impl SwapchainProperties {
pub fn find_best(
adapter: &Adapter,
surface: &SurfaceT,
) -> Result<SwapchainProperties, EnvironmentError> {
let caps = surface.capabilities(&adapter.physical_device);
let formats = surface.supported_formats(&adapter.physical_device);
// Find which settings we'll actually use based on preset preferences
let format = match formats {
Some(formats) => formats
.iter()
.find(|format| format.base_format().1 == ChannelType::Srgb)
.copied()
.ok_or(EnvironmentError::ColorFormat),
None => Ok(Format::Rgba8Srgb),
}?;
let depth_format = *[
Format::D32SfloatS8Uint,
Format::D24UnormS8Uint,
Format::D32Sfloat,
]
.iter()
.find(|format| {
format.is_depth()
&& adapter
.physical_device
.format_properties(Some(**format))
.optimal_tiling
.contains(ImageFeature::DEPTH_STENCIL_ATTACHMENT)
})
.ok_or(EnvironmentError::DepthFormat)?;
let present_mode = [
PresentMode::MAILBOX,
PresentMode::FIFO,
PresentMode::RELAXED,
PresentMode::IMMEDIATE,
]
.iter()
.cloned()
.find(|pm| caps.present_modes.contains(*pm))
.ok_or(EnvironmentError::PresentMode)?;
let composite_alpha_mode = [
CompositeAlphaMode::OPAQUE,
CompositeAlphaMode::INHERIT,
CompositeAlphaMode::PREMULTIPLIED,
CompositeAlphaMode::POSTMULTIPLIED,
]
.iter()
.cloned()
.find(|ca| caps.composite_alpha_modes.contains(*ca))
.ok_or(EnvironmentError::CompositeAlphaMode)?;
let extent = caps.extents.end().to_extent(); // Size
let viewport = Viewport {
rect: extent.rect(),
depth: 0.0..1.0,
};
Ok(SwapchainProperties {
format,
depth_format,
present_mode,
composite_alpha_mode,
extent,
viewport,
image_count: if present_mode == PresentMode::MAILBOX {
((*caps.image_count.end()) - 1).min((*caps.image_count.start()).max(3))
} else {
((*caps.image_count.end()) - 1).min((*caps.image_count.start()).max(2))
},
})
}
}
pub struct TargetChain {
/// Surface we're targeting
pub surface: ManuallyDrop<SurfaceT>,
pub properties: SwapchainProperties,
/// Resources tied to each target frame in the swapchain
pub targets: Box<[TargetResources]>,
/// Sync objects used in drawing
/// These are seperated from the targets because we don't necessarily always match up indexes
pub sync_objects: Box<[SyncObjects]>,
/// The last set of sync objects used
last_syncs: usize,
/// Last image index of the swapchain drawn to
last_image: u32,
}
impl TargetChain {
pub fn new(
device: &mut DeviceT,
adapter: &Adapter,
mut surface: SurfaceT,
cmd_pool: &mut CommandPoolT,
properties: SwapchainProperties,
) -> Result<TargetChain> {
let caps = surface.capabilities(&adapter.physical_device);
// Number of frames to pre-render
let image_count = if properties.present_mode == PresentMode::MAILBOX {
((*caps.image_count.end()) - 1).min((*caps.image_count.start()).max(3))
} else {
((*caps.image_count.end()) - 1).min((*caps.image_count.start()).max(2))
};
// Swap config
let swap_config = SwapchainConfig {
present_mode: properties.present_mode,
composite_alpha_mode: properties.composite_alpha_mode,
format: properties.format,
extent: Extent2D {
width: properties.extent.width,
height: properties.extent.height,
},
image_count,
image_layers: 1,
image_usage: ImgUsage::COLOR_ATTACHMENT,
};
let _fat = swap_config.framebuffer_attachment();
let mut targets: Vec<TargetResources> =
Vec::with_capacity(swap_config.image_count as usize);
let mut sync_objects: Vec<SyncObjects> =
Vec::with_capacity(swap_config.image_count as usize);
for _ in 0..swap_config.image_count {
targets.push(
TargetResources::new(device, cmd_pool, &properties)
.context("Error creating target resources")?,
);
sync_objects.push(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),
targets: targets.into_boxed_slice(),
sync_objects: sync_objects.into_boxed_slice(),
properties,
last_syncs: (image_count - 1) as usize, // This means the next one to be used is index 0
last_image: 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.targets.len() {
read(&self.targets[i]).deactivate(device, cmd_pool);
}
for i in 0..self.sync_objects.len() {
read(&self.sync_objects[i]).deactivate(device);
}
self.surface.unconfigure_swapchain(device);
}
unsafe { ManuallyDrop::into_inner(read(&self.surface)) }
}
pub fn do_draw_with<'a, DP: DrawPass>(
&'a mut self,
device: &mut DeviceT,
command_queue: &mut QueueT,
dp: &mut DP,
session: &Session,
) -> Result<()> {
self.last_syncs = (self.last_syncs + 1) % self.sync_objects.len();
self.last_image = (self.last_image + 1) % self.targets.len() as u32;
let syncs = &mut self.sync_objects[self.last_syncs];
let target = &mut self.targets[self.last_image as usize];
// 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 {
target.cmd_buffer.begin_primary(CommandBufferFlags::empty());
target.cmd_buffer.pipeline_barrier(
PipelineStage::TOP_OF_PIPE..PipelineStage::TOP_OF_PIPE,
Dependencies::empty(),
once(Barrier::Image {
states: (Access::empty(), Layout::Undefined)
..(Access::empty(), Layout::ColorAttachmentOptimal),
target: img.borrow(),
range: SubresourceRange {
aspects: Aspects::COLOR,
level_start: 0,
level_count: Some(1),
layer_start: 0,
layer_count: Some(1),
},
families: None,
}),
);
dp.queue_draw(session, img.borrow(), &mut target.cmd_buffer)
.context("Error in draw pass")?;
target.cmd_buffer.pipeline_barrier(
PipelineStage::BOTTOM_OF_PIPE..PipelineStage::BOTTOM_OF_PIPE,
Dependencies::empty(),
once(Barrier::Image {
states: (Access::empty(), Layout::ColorAttachmentOptimal)
..(Access::empty(), Layout::Present),
target: img.borrow(),
range: SubresourceRange {
aspects: Aspects::COLOR,
level_start: 0,
level_count: Some(1),
layer_start: 0,
layer_count: Some(1),
},
families: None,
}),
);
target.cmd_buffer.finish();
}
// Submit it
unsafe {
command_queue.submit(
once(&*target.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(())
}
}
/// Resources for a single target frame, including sync objects
pub struct TargetResources {
/// Command buffer to use when drawing
pub cmd_buffer: ManuallyDrop<CommandBufferT>,
}
impl TargetResources {
pub fn new(
_device: &mut DeviceT,
cmd_pool: &mut CommandPoolT,
_properties: &SwapchainProperties,
) -> Result<TargetResources> {
// Command Buffer
let cmd_buffer = unsafe { cmd_pool.allocate_one(hal::command::Level::Primary) };
Ok(TargetResources {
cmd_buffer: ManuallyDrop::new(cmd_buffer),
})
}
pub fn deactivate(self, _device: &mut DeviceT, cmd_pool: &mut CommandPoolT) {
use core::ptr::read;
unsafe {
cmd_pool.free(once(ManuallyDrop::into_inner(read(&self.cmd_buffer))));
}
}
}
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)));
}
}
}
|