Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vulkano util proposal #1918

Merged
merged 22 commits into from
Jun 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
- Fixed bug in various Vulkan calls where the returned data might be incomplete.
- Fixed bug that triggered an assert if a render pass had an attachment with `Undefined` initial layout.
- Added an `is_signaled` method to `FenceSignalFuture`.
- Add a simple `general_purpose_image_view` method to `StorageImage` for less verbose image view creation for e.g. intermediary render targets.
- Add a `vulkano_util` crate to help reduce boilerplate in many use cases. `VulkanoContext` to hold access to device & instances, `VulkanoWindows` to organize windows and their renderers. `VulkanoRenderer` to hold the window and methods to `acquire` (swapchain) and `present` between which you are intended to execute your pipelines.

# Version 0.29.0 (2022-03-11)

Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[workspace]
members = ["examples", "vulkano", "vulkano-shaders", "vulkano-win"]
members = ["examples", "vulkano", "vulkano-shaders", "vulkano-win", "vulkano-util"]
exclude = ["www"]
1 change: 1 addition & 0 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ winit = "0.26"
# The `vulkano_win` crate is the link between `vulkano` and `winit`. Vulkano doesn't know about winit,
# and winit doesn't know about vulkano, so import a crate that will provide a link between the two.
vulkano-win = { path = "../vulkano-win" }
vulkano-util = { path = "../vulkano-util" }

bytemuck = { version = "1.7", features = ["derive", "extern_crate_std", "min_const_generics"] }
cgmath = "0.18"
Expand Down
40 changes: 27 additions & 13 deletions examples/src/bin/interactive_fractal/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@
// notice may not be copied, modified, or distributed except
// according to those terms.

use crate::{
fractal_compute_pipeline::FractalComputePipeline,
renderer::{InterimImageView, RenderOptions, Renderer},
};
use crate::fractal_compute_pipeline::FractalComputePipeline;
use crate::place_over_frame::RenderPassPlaceOverFrame;
use cgmath::Vector2;
use std::sync::Arc;
use std::time::Instant;
use vulkano::device::Queue;
use vulkano::sync::GpuFuture;
use vulkano_util::renderer::{DeviceImageView, VulkanoWindowRenderer};
use vulkano_util::window::WindowDescriptor;
use winit::window::Fullscreen;
use winit::{
dpi::PhysicalPosition,
event::{
Expand All @@ -29,6 +32,8 @@ const MOVE_SPEED: f32 = 0.5;
pub struct FractalApp {
/// Pipeline that computes Mandelbrot & Julia fractals and writes them to an image
fractal_pipeline: FractalComputePipeline,
/// Our render pipeline (pass)
pub place_over_frame: RenderPassPlaceOverFrame,
/// Toggle that flips between julia and mandelbrot
pub is_julia: bool,
/// Togglet thats stops the movement on Julia
Expand All @@ -52,9 +57,10 @@ pub struct FractalApp {
}

impl FractalApp {
pub fn new(renderer: &Renderer) -> FractalApp {
pub fn new(gfx_queue: Arc<Queue>, image_format: vulkano::format::Format) -> FractalApp {
FractalApp {
fractal_pipeline: FractalComputePipeline::new(renderer.queue()),
fractal_pipeline: FractalComputePipeline::new(gfx_queue.clone()),
place_over_frame: RenderPassPlaceOverFrame::new(gfx_queue, image_format),
is_julia: false,
is_c_paused: false,
c: Vector2::new(0.0, 0.0),
Expand Down Expand Up @@ -87,7 +93,7 @@ Usage:
}

/// Run our compute pipeline and return a future of when the compute is finished
pub fn compute(&mut self, image_target: InterimImageView) -> Box<dyn GpuFuture> {
pub fn compute(&mut self, image_target: DeviceImageView) -> Box<dyn GpuFuture> {
self.fractal_pipeline.compute(
image_target,
self.c,
Expand Down Expand Up @@ -128,7 +134,7 @@ Usage:
}

/// Updates app state based on input state
pub fn update_state_after_inputs(&mut self, renderer: &mut Renderer) {
pub fn update_state_after_inputs(&mut self, renderer: &mut VulkanoWindowRenderer) {
// Zoom in or out
if self.input_state.scroll_delta > 0. {
self.scale /= 1.05;
Expand Down Expand Up @@ -182,12 +188,17 @@ Usage:
}
// Toggle full-screen
if self.input_state.toggle_full_screen {
renderer.toggle_full_screen()
let is_full_screen = renderer.window().fullscreen().is_some();
renderer.window().set_fullscreen(if !is_full_screen {
Some(Fullscreen::Borderless(renderer.window().current_monitor()))
} else {
None
});
}
}

/// Update input state
pub fn handle_input(&mut self, window_size: [u32; 2], event: &Event<()>) {
pub fn handle_input(&mut self, window_size: [f32; 2], event: &Event<()>) {
self.input_state.handle_input(window_size, event);
}

Expand All @@ -208,7 +219,7 @@ fn state_is_pressed(state: ElementState) -> bool {
/// Winit only has Pressed and Released events, thus continuous movement needs toggles.
/// Panning is one of those where continuous movement feels better.
struct InputState {
pub window_size: [u32; 2],
pub window_size: [f32; 2],
pub pan_up: bool,
pub pan_down: bool,
pub pan_right: bool,
Expand All @@ -227,7 +238,10 @@ struct InputState {
impl InputState {
fn new() -> InputState {
InputState {
window_size: RenderOptions::default().window_size,
window_size: [
WindowDescriptor::default().width,
WindowDescriptor::default().height,
],
pan_up: false,
pan_down: false,
pan_right: false,
Expand Down Expand Up @@ -265,7 +279,7 @@ impl InputState {
}
}

fn handle_input(&mut self, window_size: [u32; 2], event: &Event<()>) {
fn handle_input(&mut self, window_size: [f32; 2], event: &Event<()>) {
self.window_size = window_size;
if let winit::event::Event::WindowEvent { event, .. } = event {
match event {
Expand Down
24 changes: 12 additions & 12 deletions examples/src/bin/interactive_fractal/fractal_compute_pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
// notice may not be copied, modified, or distributed except
// according to those terms.

use crate::renderer::InterimImageView;
use cgmath::Vector2;
use rand::Rng;
use std::sync::Arc;
Expand All @@ -20,17 +19,18 @@ use vulkano::{
pipeline::{ComputePipeline, Pipeline, PipelineBindPoint},
sync::GpuFuture,
};
use vulkano_util::renderer::DeviceImageView;

pub struct FractalComputePipeline {
gfx_queue: Arc<Queue>,
queue: Arc<Queue>,
pipeline: Arc<ComputePipeline>,
palette: Arc<CpuAccessibleBuffer<[[f32; 4]]>>,
palette_size: i32,
end_color: [f32; 4],
}

impl FractalComputePipeline {
pub fn new(gfx_queue: Arc<Queue>) -> FractalComputePipeline {
pub fn new(queue: Arc<Queue>) -> FractalComputePipeline {
// Initial colors
let colors = vec![
[1.0, 0.0, 0.0, 1.0],
Expand All @@ -42,7 +42,7 @@ impl FractalComputePipeline {
];
let palette_size = colors.len() as i32;
let palette = CpuAccessibleBuffer::from_iter(
gfx_queue.device().clone(),
queue.device().clone(),
BufferUsage::all(),
false,
colors,
Expand All @@ -51,9 +51,9 @@ impl FractalComputePipeline {
let end_color = [0.0; 4];

let pipeline = {
let shader = cs::load(gfx_queue.device().clone()).unwrap();
let shader = cs::load(queue.device().clone()).unwrap();
ComputePipeline::new(
gfx_queue.device().clone(),
queue.device().clone(),
shader.entry_point("main").unwrap(),
&(),
None,
Expand All @@ -62,7 +62,7 @@ impl FractalComputePipeline {
.unwrap()
};
FractalComputePipeline {
gfx_queue,
queue,
pipeline,
palette,
palette_size,
Expand All @@ -81,7 +81,7 @@ impl FractalComputePipeline {
colors.push([r, g, b, a]);
}
self.palette = CpuAccessibleBuffer::from_iter(
self.gfx_queue.device().clone(),
self.queue.device().clone(),
BufferUsage::all(),
false,
colors.into_iter(),
Expand All @@ -91,7 +91,7 @@ impl FractalComputePipeline {

pub fn compute(
&mut self,
image: InterimImageView,
image: DeviceImageView,
c: Vector2<f32>,
scale: Vector2<f32>,
translation: Vector2<f32>,
Expand All @@ -111,8 +111,8 @@ impl FractalComputePipeline {
)
.unwrap();
let mut builder = AutoCommandBufferBuilder::primary(
self.gfx_queue.device().clone(),
self.gfx_queue.family(),
self.queue.device().clone(),
self.queue.family(),
CommandBufferUsage::OneTimeSubmit,
)
.unwrap();
Expand All @@ -134,7 +134,7 @@ impl FractalComputePipeline {
.dispatch([img_dims[0] / 8, img_dims[1] / 8, 1])
.unwrap();
let command_buffer = builder.build().unwrap();
let finished = command_buffer.execute(self.gfx_queue.clone()).unwrap();
let finished = command_buffer.execute(self.queue.clone()).unwrap();
finished.then_signal_fence_and_flush().unwrap().boxed()
}
}
Expand Down
83 changes: 53 additions & 30 deletions examples/src/bin/interactive_fractal/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
// notice may not be copied, modified, or distributed except
// according to those terms.

use crate::{
app::FractalApp,
renderer::{image_over_frame_renderpass, RenderOptions, Renderer},
};
use crate::app::FractalApp;
use vulkano::image::ImageUsage;
use vulkano::swapchain::PresentMode;
use vulkano::sync::GpuFuture;
use vulkano_util::context::{VulkanoConfig, VulkanoContext};
use vulkano_util::renderer::{VulkanoWindowRenderer, DEFAULT_IMAGE_FORMAT};
use vulkano_util::window::{VulkanoWindows, WindowDescriptor};
use winit::{
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
Expand All @@ -22,7 +24,6 @@ mod app;
mod fractal_compute_pipeline;
mod pixels_draw_pipeline;
mod place_over_frame;
mod renderer;

/// 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.
Expand All @@ -36,21 +37,39 @@ mod renderer;
fn main() {
// Create event loop
let mut event_loop = EventLoop::new();
// Create a renderer with a window & render options
let mut renderer = Renderer::new(
let context = VulkanoContext::new(VulkanoConfig::default());
let mut windows = VulkanoWindows::default();
let _id = windows.create_window(
&event_loop,
RenderOptions {
title: "Fractal",
..RenderOptions::default()
&context,
&WindowDescriptor {
title: "Fractal".to_string(),
present_mode: PresentMode::Immediate,
..Default::default()
},
|_| {},
);

// Add our render target image onto which we'll be rendering our fractals.
// View size None here means renderer will keep resizing the image on resize
let render_target_id = 0;
renderer.add_interim_image_view(render_target_id, None, renderer.image_format());
let primary_window_renderer = windows.get_primary_renderer_mut().unwrap();
// Make sure the image usage is correct (based on your pipeline).
primary_window_renderer.add_additional_image_view(
render_target_id,
DEFAULT_IMAGE_FORMAT,
ImageUsage {
sampled: true,
storage: true,
color_attachment: true,
transfer_dst: true,
..ImageUsage::none()
},
);

// Create app to hold the logic of our fractal explorer
let mut app = FractalApp::new(&renderer);
let gfx_queue = context.graphics_queue();
// We intend to eventually render on our swapchain, thus we use that format when creating the app here.
let mut app = FractalApp::new(gfx_queue, primary_window_renderer.swapchain_format());
app.print_guide();

// Basic loop for our runtime
Expand All @@ -60,24 +79,24 @@ fn main() {
// 4. Reset input state
// 5. Update time & title
loop {
if !handle_events(&mut event_loop, &mut renderer, &mut app) {
if !handle_events(&mut event_loop, primary_window_renderer, &mut app) {
break;
}

match renderer.window_size() {
match primary_window_renderer.window_size() {
[w, h] => {
// Skip this frame when minimized
if w == 0 || h == 0 {
if w == 0.0 || h == 0.0 {
continue;
}
}
}

app.update_state_after_inputs(&mut renderer);
compute_then_render(&mut renderer, &mut app, render_target_id);
app.update_state_after_inputs(primary_window_renderer);
compute_then_render(primary_window_renderer, &mut app, render_target_id);
app.reset_input_state();
app.update_time();
renderer.window().set_title(&format!(
primary_window_renderer.window().set_title(&format!(
"{} fps: {:.2} dt: {:.2}, Max Iterations: {}",
if app.is_julia { "Julia" } else { "Mandelbrot" },
app.avg_fps(),
Expand All @@ -90,7 +109,7 @@ fn main() {
/// Handle events and return `bool` if we should quit
fn handle_events(
event_loop: &mut EventLoop<()>,
renderer: &mut Renderer,
renderer: &mut VulkanoWindowRenderer,
app: &mut FractalApp,
) -> bool {
let mut is_running = true;
Expand All @@ -114,24 +133,28 @@ fn handle_events(
}

/// Orchestrate rendering here
fn compute_then_render(renderer: &mut Renderer, app: &mut FractalApp, target_image_id: usize) {
fn compute_then_render(
renderer: &mut VulkanoWindowRenderer,
app: &mut FractalApp,
target_image_id: usize,
) {
// Start frame
let before_pipeline_future = match renderer.start_frame() {
let before_pipeline_future = match renderer.acquire() {
Err(e) => {
println!("{}", e.to_string());
return;
}
Ok(future) => future,
};
// Retrieve target image
let target_image = renderer.get_interim_image_view(target_image_id);
let image = renderer.get_additional_image_view(target_image_id);
// Compute our fractal (writes to target image). Join future with `before_pipeline_future`.
let after_compute = app
.compute(target_image.clone())
.join(before_pipeline_future);
// Render target image over frame. Input previous future.
let after_compute = app.compute(image.clone()).join(before_pipeline_future);
// Render image over frame. Input previous future. Draw on swapchain image
let after_renderpass_future =
image_over_frame_renderpass(renderer, after_compute, target_image);
// Finish frame (which presents the view). Input last future
renderer.finish_frame(after_renderpass_future);
app.place_over_frame
.render(after_compute, image, renderer.swapchain_image_view());
// Finish frame (which presents the view). Input last future. Wait for the future so resources are not in use
// when we render
renderer.present(after_renderpass_future, true);
}
Loading