2023-03-05 18:56:35 +00:00
|
|
|
// This is an example demonstrating an application with some more non-trivial functionality.
|
|
|
|
// It should get you more up to speed with how you can use Vulkano.
|
|
|
|
//
|
|
|
|
// It contains:
|
|
|
|
//
|
|
|
|
// - A compute pipeline to calculate Mandelbrot and Julia fractals writing them to an image.
|
|
|
|
// - A graphics pipeline to draw the fractal image over a quad that covers the whole screen.
|
|
|
|
// - A renderpass rendering that image on the swapchain image.
|
|
|
|
// - An organized renderer with functionality good enough to copy to other projects.
|
|
|
|
// - A simple `FractalApp` to handle runtime state.
|
|
|
|
// - A simple `InputState` to interact with the application.
|
|
|
|
|
2022-06-24 15:27:33 +00:00
|
|
|
use crate::app::FractalApp;
|
2023-10-31 17:47:17 +00:00
|
|
|
use std::error::Error;
|
2023-03-05 18:56:35 +00:00
|
|
|
use vulkano::{image::ImageUsage, swapchain::PresentMode, sync::GpuFuture};
|
|
|
|
use vulkano_util::{
|
|
|
|
context::{VulkanoConfig, VulkanoContext},
|
|
|
|
renderer::{VulkanoWindowRenderer, DEFAULT_IMAGE_FORMAT},
|
|
|
|
window::{VulkanoWindows, WindowDescriptor},
|
|
|
|
};
|
2021-09-13 15:14:54 +00:00
|
|
|
use winit::{
|
|
|
|
event::{Event, WindowEvent},
|
|
|
|
event_loop::{ControlFlow, EventLoop},
|
|
|
|
};
|
|
|
|
|
|
|
|
mod app;
|
|
|
|
mod fractal_compute_pipeline;
|
|
|
|
mod pixels_draw_pipeline;
|
|
|
|
mod place_over_frame;
|
|
|
|
|
2023-10-31 17:47:17 +00:00
|
|
|
fn main() -> Result<(), impl Error> {
|
2023-03-05 18:56:35 +00:00
|
|
|
// Create the event loop.
|
2023-10-31 17:47:17 +00:00
|
|
|
let event_loop = EventLoop::new().unwrap();
|
2022-06-24 15:27:33 +00:00
|
|
|
let context = VulkanoContext::new(VulkanoConfig::default());
|
|
|
|
let mut windows = VulkanoWindows::default();
|
|
|
|
let _id = windows.create_window(
|
2021-09-13 15:14:54 +00:00
|
|
|
&event_loop,
|
2022-06-24 15:27:33 +00:00
|
|
|
&context,
|
|
|
|
&WindowDescriptor {
|
|
|
|
title: "Fractal".to_string(),
|
2022-09-17 15:37:22 +00:00
|
|
|
present_mode: PresentMode::Fifo,
|
2022-06-24 15:27:33 +00:00
|
|
|
..Default::default()
|
2021-09-13 15:14:54 +00:00
|
|
|
},
|
2022-06-24 15:27:33 +00:00
|
|
|
|_| {},
|
2021-09-13 15:14:54 +00:00
|
|
|
);
|
2022-06-24 15:27:33 +00:00
|
|
|
|
2021-09-13 15:14:54 +00:00
|
|
|
// Add our render target image onto which we'll be rendering our fractals.
|
|
|
|
let render_target_id = 0;
|
2022-06-24 15:27:33 +00:00
|
|
|
let primary_window_renderer = windows.get_primary_renderer_mut().unwrap();
|
2023-03-05 18:56:35 +00:00
|
|
|
|
2022-06-24 15:27:33 +00:00
|
|
|
// Make sure the image usage is correct (based on your pipeline).
|
|
|
|
primary_window_renderer.add_additional_image_view(
|
|
|
|
render_target_id,
|
|
|
|
DEFAULT_IMAGE_FORMAT,
|
2022-11-07 07:48:25 +00:00
|
|
|
ImageUsage::SAMPLED | ImageUsage::STORAGE | ImageUsage::TRANSFER_DST,
|
2022-06-24 15:27:33 +00:00
|
|
|
);
|
2021-09-13 15:14:54 +00:00
|
|
|
|
2023-03-05 18:56:35 +00:00
|
|
|
// Create app to hold the logic of our fractal explorer.
|
2022-06-24 15:27:33 +00:00
|
|
|
let gfx_queue = context.graphics_queue();
|
2023-03-05 18:56:35 +00:00
|
|
|
|
|
|
|
// We intend to eventually render on our swapchain, thus we use that format when creating the
|
|
|
|
// app here.
|
2022-07-30 06:53:52 +00:00
|
|
|
let mut app = FractalApp::new(
|
|
|
|
gfx_queue.clone(),
|
|
|
|
primary_window_renderer.swapchain_format(),
|
2024-02-21 08:08:50 +00:00
|
|
|
primary_window_renderer.swapchain_image_views(),
|
2022-07-30 06:53:52 +00:00
|
|
|
);
|
2021-09-13 15:14:54 +00:00
|
|
|
app.print_guide();
|
|
|
|
|
2023-10-31 17:47:17 +00:00
|
|
|
event_loop.run(move |event, elwt| {
|
|
|
|
elwt.set_control_flow(ControlFlow::Poll);
|
|
|
|
|
2023-06-04 07:15:21 +00:00
|
|
|
let renderer = windows.get_primary_renderer_mut().unwrap();
|
2022-05-09 12:25:26 +00:00
|
|
|
|
2023-06-04 07:15:21 +00:00
|
|
|
if process_event(renderer, &event, &mut app, render_target_id) {
|
2023-10-31 17:47:17 +00:00
|
|
|
elwt.exit();
|
2023-06-04 07:15:21 +00:00
|
|
|
return;
|
2022-05-09 12:25:26 +00:00
|
|
|
}
|
|
|
|
|
2023-06-04 07:15:21 +00:00
|
|
|
// Pass event for the app to handle our inputs.
|
|
|
|
app.handle_input(renderer.window_size(), &event);
|
|
|
|
})
|
2021-09-13 15:14:54 +00:00
|
|
|
}
|
|
|
|
|
2023-06-04 07:15:21 +00:00
|
|
|
/// Processes a single event for an event loop.
|
|
|
|
/// Returns true only if the window is to be closed.
|
|
|
|
pub fn process_event(
|
2022-06-24 15:27:33 +00:00
|
|
|
renderer: &mut VulkanoWindowRenderer,
|
2023-06-04 07:15:21 +00:00
|
|
|
event: &Event<()>,
|
2021-09-13 15:14:54 +00:00
|
|
|
app: &mut FractalApp,
|
2023-06-04 07:15:21 +00:00
|
|
|
render_target_id: usize,
|
2021-09-13 15:14:54 +00:00
|
|
|
) -> bool {
|
2023-06-04 07:15:21 +00:00
|
|
|
match &event {
|
2023-10-31 17:47:17 +00:00
|
|
|
Event::WindowEvent {
|
|
|
|
event: WindowEvent::CloseRequested,
|
|
|
|
..
|
|
|
|
} => {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
Event::WindowEvent {
|
|
|
|
event: WindowEvent::Resized(..) | WindowEvent::ScaleFactorChanged { .. },
|
|
|
|
..
|
|
|
|
} => renderer.resize(),
|
|
|
|
Event::WindowEvent {
|
|
|
|
event: WindowEvent::RedrawRequested,
|
|
|
|
..
|
|
|
|
} => 'redraw: {
|
2023-06-04 07:15:21 +00:00
|
|
|
// Tasks for redrawing:
|
|
|
|
// 1. Update state based on events
|
|
|
|
// 2. Compute & Render
|
|
|
|
// 3. Reset input state
|
|
|
|
// 4. Update time & title
|
|
|
|
|
|
|
|
// The rendering part goes here:
|
|
|
|
match renderer.window_size() {
|
|
|
|
[w, h] => {
|
|
|
|
// Skip this frame when minimized.
|
|
|
|
if w == 0.0 || h == 0.0 {
|
|
|
|
break 'redraw;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
app.update_state_after_inputs(renderer);
|
|
|
|
compute_then_render(renderer, app, render_target_id);
|
|
|
|
app.reset_input_state();
|
|
|
|
app.update_time();
|
|
|
|
renderer.window().set_title(&format!(
|
|
|
|
"{} fps: {:.2} dt: {:.2}, Max Iterations: {}",
|
|
|
|
if app.is_julia { "Julia" } else { "Mandelbrot" },
|
|
|
|
app.avg_fps(),
|
|
|
|
app.dt(),
|
|
|
|
app.max_iters
|
|
|
|
));
|
2021-09-13 15:14:54 +00:00
|
|
|
}
|
2023-10-31 17:47:17 +00:00
|
|
|
Event::AboutToWait => renderer.window().request_redraw(),
|
2023-06-04 07:15:21 +00:00
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
!app.is_running()
|
2021-09-13 15:14:54 +00:00
|
|
|
}
|
|
|
|
|
2023-03-05 18:56:35 +00:00
|
|
|
/// Orchestrates rendering.
|
2022-06-24 15:27:33 +00:00
|
|
|
fn compute_then_render(
|
|
|
|
renderer: &mut VulkanoWindowRenderer,
|
|
|
|
app: &mut FractalApp,
|
|
|
|
target_image_id: usize,
|
|
|
|
) {
|
2023-03-05 18:56:35 +00:00
|
|
|
// Start the frame.
|
2024-02-21 08:08:50 +00:00
|
|
|
let before_pipeline_future = match renderer.acquire(|swapchain_image_views| {
|
|
|
|
app.place_over_frame
|
|
|
|
.recreate_framebuffers(swapchain_image_views)
|
|
|
|
}) {
|
2021-09-13 15:14:54 +00:00
|
|
|
Err(e) => {
|
2023-01-03 17:49:12 +00:00
|
|
|
println!("{e}");
|
2021-09-13 15:14:54 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
Ok(future) => future,
|
|
|
|
};
|
2023-03-05 18:56:35 +00:00
|
|
|
|
|
|
|
// Retrieve the target image.
|
2022-06-24 15:27:33 +00:00
|
|
|
let image = renderer.get_additional_image_view(target_image_id);
|
2023-03-05 18:56:35 +00:00
|
|
|
|
2021-09-13 15:14:54 +00:00
|
|
|
// Compute our fractal (writes to target image). Join future with `before_pipeline_future`.
|
2022-06-24 15:27:33 +00:00
|
|
|
let after_compute = app.compute(image.clone()).join(before_pipeline_future);
|
2023-03-05 18:56:35 +00:00
|
|
|
|
|
|
|
// Render the image over the swapchain image, inputting the previous future.
|
2024-02-21 08:08:50 +00:00
|
|
|
let after_renderpass_future = app.place_over_frame.render(
|
|
|
|
after_compute,
|
|
|
|
image,
|
|
|
|
renderer.swapchain_image_view(),
|
|
|
|
renderer.image_index(),
|
|
|
|
);
|
2023-03-05 18:56:35 +00:00
|
|
|
|
|
|
|
// Finish the frame (which presents the view), inputting the last future. Wait for the future
|
|
|
|
// so resources are not in use when we render.
|
2022-06-24 15:27:33 +00:00
|
|
|
renderer.present(after_renderpass_future, true);
|
2021-09-13 15:14:54 +00:00
|
|
|
}
|