Skip to content

Commit

Permalink
Change RefreshSync loop mode to wait on previous frame presentation
Browse files Browse the repository at this point in the history
This should fix the issue where the GPU would lag behind the CPU on
macos.

For **v0.9**.
  • Loading branch information
mitchmindtree committed Jan 2, 2019
1 parent 37de05a commit 944c8f6
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 34 deletions.
62 changes: 36 additions & 26 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1501,14 +1501,14 @@ where
// This method cleans up any unused resources associated with this GPU future.
fn cleanup_unused_gpu_resources_for_window(app: &App, window_id: window::Id) {
let windows = app.windows.borrow();
windows[&window_id]
let mut guard = windows[&window_id]
.swapchain
.previous_frame_end
.lock()
.expect("failed to lock `previous_frame_end`")
.as_mut()
.expect("`previous_frame_end` was `None`")
.cleanup_finished();
.expect("failed to lock `previous_frame_end`");
if let Some(future) = guard.as_mut() {
future.cleanup_finished();
}
}

// Returns `true` if the window's swapchain needs to be recreated.
Expand Down Expand Up @@ -2008,43 +2008,53 @@ where

let mut windows = app.windows.borrow_mut();
let window = windows.get_mut(&window_id).expect("no window for id");
let future = window

// Wait for the previous frame presentation to be finished to avoid out-pacing the GPU on macos.
if let Some(mut previous_frame_fence_signal_future) = window
.swapchain
.previous_frame_end
.lock()
.expect("failed to lock `previous_frame_end`")
.take()
.expect("`previous_frame_end` was `None`")
.join(swapchain_image_acquire_future)
.then_execute(queue.clone(), command_buffer)
.expect("failed to execute future")
// The image color output is now expected to contain the user's graphics.
// But in order to show it on the screen, we have to `present` the image.
.then_swapchain_present(
queue.clone(),
swapchain.swapchain.clone(),
swapchain_image_index,
)
// Flush forwards the future to the GPU to begin the actual processing.
.then_signal_fence_and_flush();
let previous_frame_end = match future {
Ok(future) => {
Some(Box::new(future) as Box<_>)
}
{
previous_frame_fence_signal_future.cleanup_finished();
previous_frame_fence_signal_future
.wait(None)
.expect("failed to wait for `previous_frame_end` future to signal fence");
}

// The future associated with the end of the current frame.
let future_result = {
let present_future = swapchain_image_acquire_future
.then_execute(queue.clone(), command_buffer)
.expect("failed to execute future")
.then_swapchain_present(
queue.clone(),
swapchain.swapchain.clone(),
swapchain_image_index,
);
(Box::new(present_future) as Box<GpuFuture>)
.then_signal_fence_and_flush()
};

// Handle the result of the future.
let current_frame_end = match future_result {
Ok(future) => Some(future),
Err(vulkano::sync::FlushError::OutOfDate) => {
window.swapchain.needs_recreation.store(true, atomic::Ordering::Relaxed);
Some(Box::new(vulkano::sync::now(queue.device().clone())) as Box<_>)
None
}
Err(e) => {
println!("{:?}", e);
Some(Box::new(vulkano::sync::now(queue.device().clone())) as Box<_>)
None
}
};

*window
.swapchain
.previous_frame_end
.lock()
.expect("failed to acquire `previous_frame_end` lock") = previous_frame_end;
.expect("failed to acquire `previous_frame_end` lock") = current_frame_end;
}

// Acquire the next swapchain image for the given window and draw to it using the user's
Expand Down
11 changes: 3 additions & 8 deletions src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use vulkano::framebuffer::{AttachmentsList, Framebuffer, FramebufferAbstract, Fr
use vulkano::instance::PhysicalDevice;
use vulkano::swapchain::{ColorSpace, CompositeAlpha, PresentMode, SurfaceTransform,
SwapchainCreationError};
use vulkano::sync::GpuFuture;
use vulkano::sync::{FenceSignalFuture, GpuFuture};
use vulkano_win::{VkSurfaceBuild};
use winit::{self, MonitorId, MouseCursor};
use winit::dpi::LogicalSize;
Expand Down Expand Up @@ -251,11 +251,7 @@ pub(crate) struct WindowSwapchain {
//
// Destroying the `GpuFuture` blocks until the GPU is finished executing it. In order to avoid
// that, we store the submission of the previous frame here.
//
// This is initialised to `Some(vulkano::sync::now(device))`. An `Option` is used to allow for
// taking ownership in the application loop where we are required to join `previous_frame_end`
// with the future associated with acquiring an image from the GPU.
pub(crate) previous_frame_end: Mutex<Option<Box<GpuFuture>>>,
pub(crate) previous_frame_end: Mutex<Option<FenceSignalFuture<Box<GpuFuture>>>>,
}

/// Swapchain building parameters for which Nannou will provide a default if unspecified.
Expand Down Expand Up @@ -955,8 +951,7 @@ impl<'app> Builder<'app> {

let window_id = surface.window().id();
let needs_recreation = AtomicBool::new(false);
let now = Box::new(vulkano::sync::now(queue.device().clone())) as Box<GpuFuture>;
let previous_frame_end = Mutex::new(Some(now));
let previous_frame_end = Mutex::new(None);
let frame_count = 0;
let swapchain = Arc::new(WindowSwapchain {
needs_recreation,
Expand Down

0 comments on commit 944c8f6

Please sign in to comment.