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
//! The lower-level "raw" frame type allowing to draw directly to the window's swap chain image.
use crate::geom;
use crate::wgpu;
use crate::window;
use std::cell::{RefCell, RefMut};
use std::sync::Arc;
/// Allows the user to draw a single **RawFrame** to the surface of a window.
///
/// The application's **view** function is called each time the application is ready to retrieve a
/// new image that will be displayed to a window. The **RawFrame** type can be thought of as the
/// canvas to which you draw this image.
///
/// ## Under the hood - WGPU
///
/// **RawFrame** provides access to the **wgpu::TextureViewHandle** associated with the swap
/// chain's current target texture for a single window.
///
/// In the case that your **view** function is shared between multiple windows, can determine which
/// window the **RawFrame** is associated with via the **RawFrame::window_id** method.
///
/// The user can draw to the swap chain texture by building a list of commands via a
/// `wgpu::CommandEncoder` and submitting them to the `wgpu::Queue` associated with the
/// `wgpu::Device` that was used to create the swap chain. It is important that the queue
/// matches the device. In an effort to reduce the chance for errors to occur, **RawFrame**
/// provides access to a `wgpu::CommandEncoder` whose commands are guaranteed to be submitted to
/// the correct `wgpu::Queue` at the end of the **view** function.
pub struct RawFrame<'swap_chain> {
command_encoder: Option<RefCell<wgpu::CommandEncoder>>,
window_id: window::Id,
nth: u64,
swap_chain_texture: &'swap_chain wgpu::TextureViewHandle,
device_queue_pair: Arc<wgpu::DeviceQueuePair>,
texture_format: wgpu::TextureFormat,
window_rect: geom::Rect,
}
impl<'swap_chain> RawFrame<'swap_chain> {
// Initialise a new empty frame ready for "drawing".
pub(crate) fn new_empty(
device_queue_pair: Arc<wgpu::DeviceQueuePair>,
window_id: window::Id,
nth: u64,
swap_chain_texture: &'swap_chain wgpu::TextureViewHandle,
texture_format: wgpu::TextureFormat,
window_rect: geom::Rect,
) -> Self {
let ce_desc = wgpu::CommandEncoderDescriptor {
label: Some("nannou_raw_frame"),
};
let command_encoder = device_queue_pair.device().create_command_encoder(&ce_desc);
let command_encoder = Some(RefCell::new(command_encoder));
let frame = RawFrame {
command_encoder,
window_id,
nth,
swap_chain_texture,
device_queue_pair,
texture_format,
window_rect,
};
frame
}
// Submit the encoded commands to the queue of the device that was used to create the swap
// chain texture.
pub(crate) fn submit_inner(&mut self) {
let command_encoder = self
.command_encoder
.take()
.expect("the command encoder should always be `Some` at the time of submission")
.into_inner();
let command_buffer = command_encoder.finish();
let queue = self.device_queue_pair.queue();
queue.submit(std::iter::once(command_buffer));
}
// Allow the `Frame` to check if the raw frame has already been submitted on drop.
pub(crate) fn is_submitted(&self) -> bool {
self.command_encoder.is_none()
}
/// Access the command encoder in order to encode commands that will be submitted to the swap
/// chain queue at the end of the call to **view**.
pub fn command_encoder(&self) -> RefMut<wgpu::CommandEncoder> {
match self.command_encoder {
Some(ref ce) => ce.borrow_mut(),
None => unreachable!("`RawFrame`'s command_encoder was `None`"),
}
}
/// The `Id` of the window whose wgpu surface is associated with this frame.
pub fn window_id(&self) -> window::Id {
self.window_id
}
/// A **Rect** representing the full surface of the frame.
///
/// The returned **Rect** is equivalent to the result of calling **Window::rect** on the window
/// associated with this **Frame**.
pub fn rect(&self) -> geom::Rect {
self.window_rect
}
/// The `nth` frame for the associated window since the application started.
///
/// E.g. the first frame yielded will return `0`, the second will return `1`, and so on.
pub fn nth(&self) -> u64 {
self.nth
}
/// The swap chain texture that will be the target for drawing this frame.
pub fn swap_chain_texture(&self) -> &wgpu::TextureViewHandle {
&self.swap_chain_texture
}
/// The texture format of the frame's swap chain texture.
pub fn texture_format(&self) -> wgpu::TextureFormat {
self.texture_format
}
/// The device and queue on which the swap chain was created and which will be used to submit
/// the **RawFrame**'s encoded commands.
///
/// This refers to the same **DeviceQueuePair** as held by the window associated with this
/// frame.
pub fn device_queue_pair(&self) -> &Arc<wgpu::DeviceQueuePair> {
&self.device_queue_pair
}
/// Submit the frame to the GPU!
///
/// Specifically, this submits the encoded commands to the queue of the device that was used to
/// create the swap chain texture.
///
/// Note: You do not need to call this manually as submission will occur automatically when
/// the **Frame** is dropped.
///
/// Note: Be careful that you do not currently possess a lock to either the frame's command
/// encoder *or* the queue of the window associated with this frame or this method will lock
/// and block forever.
pub fn submit(mut self) {
self.submit_inner();
}
/// Clear the texture with the given color.
pub fn clear(&self, texture_view: &wgpu::TextureView, color: wgpu::Color) {
wgpu::clear_texture(texture_view, color, &mut *self.command_encoder())
}
}
impl<'swap_chain> Drop for RawFrame<'swap_chain> {
fn drop(&mut self) {
// Submit the commands if the user hasn't done so already.
if !self.is_submitted() {
self.submit_inner();
}
}
}