Compare commits
1 commit
Author | SHA1 | Date | |
---|---|---|---|
00f976dda3 |
74 changed files with 1492 additions and 6538 deletions
1682
Cargo.lock
generated
1682
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
47
Cargo.toml
47
Cargo.toml
|
@ -5,32 +5,43 @@ edition = "2024"
|
||||||
authors = ["Florian RICHER <florian.richer@protonmail.com>"]
|
authors = ["Florian RICHER <florian.richer@protonmail.com>"]
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[workspace]
|
||||||
|
resolver = "2"
|
||||||
|
members = ["crates/*"]
|
||||||
|
|
||||||
|
[workspace.dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
thiserror = "2.0"
|
thiserror = "2.0"
|
||||||
winit = { version = "0.30", features = ["rwh_06"] }
|
winit = { version = "0.30", features = ["rwh_06"] }
|
||||||
|
|
||||||
vulkano = "0.35"
|
vulkano = "0.35"
|
||||||
vulkano-shaders = "0.35"
|
vulkano-shaders = "0.35"
|
||||||
vulkano-util = "0.35"
|
|
||||||
egui_winit_vulkano = { version = "0.28" }
|
|
||||||
|
|
||||||
image = { version = "0.25", features = ["png", "jpeg"] }
|
|
||||||
|
|
||||||
# Math
|
# Math
|
||||||
glam = { version = "0.30" }
|
glam = { version = "0.30" }
|
||||||
|
|
||||||
# Log and tracing
|
|
||||||
tracing = "0.1"
|
|
||||||
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
|
|
||||||
tracing-log = "0.2"
|
|
||||||
tracing-tracy = "0.11"
|
|
||||||
|
|
||||||
# Random
|
|
||||||
rand = "0.9"
|
|
||||||
|
|
||||||
# OBJ loader
|
|
||||||
tobj = "4.0"
|
|
||||||
|
|
||||||
# ECS
|
# ECS
|
||||||
bevy_ecs = "0.16.1" # Latest version
|
bevy_ecs = "0.16"
|
||||||
|
bevy_app = "0.16"
|
||||||
|
|
||||||
|
# Log and tracing
|
||||||
|
log = "0.4"
|
||||||
|
env_logger = "0.11"
|
||||||
|
|
||||||
|
engine_vulkan = { path = "crates/engine_vulkan" }
|
||||||
|
engine_window = { path = "crates/engine_window" }
|
||||||
|
engine_render = { path = "crates/engine_render" }
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
log = { workspace = true }
|
||||||
|
env_logger = { workspace = true }
|
||||||
|
bevy_app = { workspace = true }
|
||||||
|
bevy_ecs = { workspace = true }
|
||||||
|
winit = { workspace = true }
|
||||||
|
vulkano = { workspace = true }
|
||||||
|
vulkano-shaders = { workspace = true }
|
||||||
|
glam = { workspace = true }
|
||||||
|
|
||||||
|
engine_vulkan = { workspace = true }
|
||||||
|
engine_window = { workspace = true }
|
||||||
|
engine_render = { workspace = true }
|
||||||
|
|
11
README.md
11
README.md
|
@ -1,16 +1,5 @@
|
||||||
# Project
|
# Project
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
1. Run renderdoc on wayland:
|
|
||||||
|
|
||||||
```console
|
|
||||||
WAYLAND_DISPLAY= QT_QPA_PLATFORM=xcb qrenderdoc
|
|
||||||
```
|
|
||||||
> Not supported yet https://github.com/baldurk/renderdoc/issues/853
|
|
||||||
|
|
||||||
2. [Difference Between OpenGL and Vulkan](./docs/OPENGL_VULKAN_DIFF.md)
|
|
||||||
|
|
||||||
## Usefull links
|
## Usefull links
|
||||||
|
|
||||||
- https://vulkan-tutorial.com/fr/Introduction
|
- https://vulkan-tutorial.com/fr/Introduction
|
||||||
|
|
12
crates/engine_render/Cargo.toml
Normal file
12
crates/engine_render/Cargo.toml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
[package]
|
||||||
|
name = "engine_render"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
log = { workspace = true }
|
||||||
|
bevy_app = { workspace = true }
|
||||||
|
bevy_ecs = { workspace = true }
|
||||||
|
vulkano = { workspace = true }
|
||||||
|
engine_vulkan = { workspace = true }
|
||||||
|
engine_window = { workspace = true }
|
120
crates/engine_render/src/lib.rs
Normal file
120
crates/engine_render/src/lib.rs
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
use bevy_app::{App, AppLabel, Last, Plugin, SubApp};
|
||||||
|
use bevy_ecs::{
|
||||||
|
schedule::{IntoScheduleConfigs, Schedule, ScheduleLabel, SystemSet},
|
||||||
|
system::{Commands, Res},
|
||||||
|
world::World,
|
||||||
|
};
|
||||||
|
use engine_vulkan::{
|
||||||
|
VulkanCommandBufferAllocator, VulkanDescriptorSetAllocator, VulkanDevice, VulkanGraphicsQueue,
|
||||||
|
VulkanInstance, VulkanMemoryAllocator,
|
||||||
|
};
|
||||||
|
use engine_window::raw_handle::WindowWrapper;
|
||||||
|
use window::WindowRenderPlugin;
|
||||||
|
|
||||||
|
pub mod render;
|
||||||
|
pub mod window;
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
|
||||||
|
pub enum RenderSystems {
|
||||||
|
ManageViews,
|
||||||
|
Prepare,
|
||||||
|
Queue,
|
||||||
|
Render,
|
||||||
|
Present,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone, Default)]
|
||||||
|
pub struct Render;
|
||||||
|
|
||||||
|
impl Render {
|
||||||
|
pub fn base_schedule() -> Schedule {
|
||||||
|
use RenderSystems::*;
|
||||||
|
|
||||||
|
let mut schedule = Schedule::new(Self);
|
||||||
|
|
||||||
|
schedule.configure_sets((ManageViews, Prepare, Queue, Render, Present).chain());
|
||||||
|
|
||||||
|
schedule
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, AppLabel)]
|
||||||
|
pub struct RenderApp;
|
||||||
|
|
||||||
|
pub struct RenderPlugin;
|
||||||
|
|
||||||
|
impl Plugin for RenderPlugin {
|
||||||
|
fn build(&self, _app: &mut App) {}
|
||||||
|
|
||||||
|
fn ready(&self, app: &App) -> bool {
|
||||||
|
let world = app.world();
|
||||||
|
|
||||||
|
world.get_resource::<WindowWrapper>().is_some()
|
||||||
|
&& world.get_resource::<VulkanInstance>().is_some()
|
||||||
|
&& world.get_resource::<VulkanDevice>().is_some()
|
||||||
|
&& world.get_resource::<VulkanGraphicsQueue>().is_some()
|
||||||
|
&& world.get_resource::<VulkanMemoryAllocator>().is_some()
|
||||||
|
&& world
|
||||||
|
.get_resource::<VulkanCommandBufferAllocator>()
|
||||||
|
.is_some()
|
||||||
|
&& world
|
||||||
|
.get_resource::<VulkanDescriptorSetAllocator>()
|
||||||
|
.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish(&self, app: &mut App) {
|
||||||
|
let mut render_app = SubApp::new();
|
||||||
|
render_app.update_schedule = Some(Render.intern());
|
||||||
|
render_app.add_schedule(Render::base_schedule());
|
||||||
|
render_app.add_systems(
|
||||||
|
Render,
|
||||||
|
render::render_system
|
||||||
|
.in_set(RenderSystems::Render)
|
||||||
|
.run_if(render::can_render),
|
||||||
|
);
|
||||||
|
|
||||||
|
extract_app_resources(app.world_mut(), render_app.world_mut());
|
||||||
|
|
||||||
|
app.insert_sub_app(RenderApp, render_app);
|
||||||
|
|
||||||
|
app.add_plugins(WindowRenderPlugin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_app_resources(world: &mut World, render_world: &mut World) {
|
||||||
|
let window_wrapper = world
|
||||||
|
.get_resource::<WindowWrapper>()
|
||||||
|
.expect("Failed to get WindowWrapper. Check is WindowPlugin is added before RenderPlugin.");
|
||||||
|
|
||||||
|
let vulkan_instance = world.get_resource::<VulkanInstance>().expect(
|
||||||
|
"Failed to get Vulkan instance. Check is VulkanPlugin is added before RenderPlugin.",
|
||||||
|
);
|
||||||
|
|
||||||
|
let vulkan_device = world
|
||||||
|
.get_resource::<VulkanDevice>()
|
||||||
|
.expect("Failed to get Vulkan device. Check is VulkanPlugin is added before RenderPlugin.");
|
||||||
|
|
||||||
|
let vulkan_graphics_queue = world.get_resource::<VulkanGraphicsQueue>().expect(
|
||||||
|
"Failed to get Vulkan graphics queue. Check is VulkanPlugin is added before RenderPlugin.",
|
||||||
|
);
|
||||||
|
|
||||||
|
let vulkan_memory_allocator = world
|
||||||
|
.get_resource::<VulkanMemoryAllocator>()
|
||||||
|
.expect("Failed to get Vulkan memory allocator. Check is VulkanPlugin is added before RenderPlugin.");
|
||||||
|
|
||||||
|
let vulkan_command_buffer_allocator = world
|
||||||
|
.get_resource::<VulkanCommandBufferAllocator>()
|
||||||
|
.expect("Failed to get Vulkan command buffer allocator. Check is VulkanPlugin is added before RenderPlugin.");
|
||||||
|
|
||||||
|
let vulkan_descriptor_set_allocator = world
|
||||||
|
.get_resource::<VulkanDescriptorSetAllocator>()
|
||||||
|
.expect("Failed to get Vulkan descriptor set allocator. Check is VulkanPlugin is added before RenderPlugin.");
|
||||||
|
|
||||||
|
render_world.insert_resource(vulkan_instance.clone());
|
||||||
|
render_world.insert_resource(vulkan_device.clone());
|
||||||
|
render_world.insert_resource(vulkan_graphics_queue.clone());
|
||||||
|
render_world.insert_resource(vulkan_memory_allocator.clone());
|
||||||
|
render_world.insert_resource(vulkan_command_buffer_allocator.clone());
|
||||||
|
render_world.insert_resource(vulkan_descriptor_set_allocator.clone());
|
||||||
|
render_world.insert_resource(window_wrapper.clone());
|
||||||
|
}
|
122
crates/engine_render/src/render.rs
Normal file
122
crates/engine_render/src/render.rs
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
use bevy_ecs::system::{Res, ResMut};
|
||||||
|
use engine_vulkan::{VulkanCommandBufferAllocator, VulkanDevice, VulkanGraphicsQueue};
|
||||||
|
use vulkano::{
|
||||||
|
Validated, VulkanError,
|
||||||
|
command_buffer::{
|
||||||
|
AutoCommandBufferBuilder, CommandBufferUsage, RenderingAttachmentInfo, RenderingInfo,
|
||||||
|
},
|
||||||
|
render_pass::{AttachmentLoadOp, AttachmentStoreOp},
|
||||||
|
swapchain::{SwapchainAcquireFuture, SwapchainPresentInfo, acquire_next_image},
|
||||||
|
sync::{self, GpuFuture},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::window::{WindowSurface, WindowSurfaceData};
|
||||||
|
|
||||||
|
pub fn can_render(window_surface: Res<WindowSurface>) -> bool {
|
||||||
|
window_surface.surface.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_system(
|
||||||
|
mut window_surface: ResMut<WindowSurface>,
|
||||||
|
command_buffer_allocator: Res<VulkanCommandBufferAllocator>,
|
||||||
|
graphics_queue: Res<VulkanGraphicsQueue>,
|
||||||
|
device: Res<VulkanDevice>,
|
||||||
|
) {
|
||||||
|
{
|
||||||
|
let surface = window_surface.surface.as_ref().unwrap();
|
||||||
|
if surface.viewport.extent[0] == 0.0 || surface.viewport.extent[1] == 0.0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let (image_index, acquire_future) =
|
||||||
|
match acquire_image(&mut window_surface.surface.as_mut().unwrap()) {
|
||||||
|
Some(r) => r,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut builder = AutoCommandBufferBuilder::primary(
|
||||||
|
command_buffer_allocator.0.clone(),
|
||||||
|
graphics_queue.0.queue_family_index(),
|
||||||
|
CommandBufferUsage::OneTimeSubmit,
|
||||||
|
)
|
||||||
|
.expect("failed to create command buffer builder");
|
||||||
|
|
||||||
|
{
|
||||||
|
let surface = window_surface.surface.as_ref().unwrap();
|
||||||
|
builder
|
||||||
|
.begin_rendering(RenderingInfo {
|
||||||
|
color_attachments: vec![Some(RenderingAttachmentInfo {
|
||||||
|
load_op: AttachmentLoadOp::Clear,
|
||||||
|
store_op: AttachmentStoreOp::Store,
|
||||||
|
clear_value: Some([0.0, 0.0, 0.0, 0.0].into()),
|
||||||
|
..RenderingAttachmentInfo::image_view(
|
||||||
|
surface.attachment_image_views[image_index as usize].clone(),
|
||||||
|
)
|
||||||
|
})],
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
.set_viewport(0, [surface.viewport.clone()].into_iter().collect())
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.end_rendering().unwrap();
|
||||||
|
|
||||||
|
let command_buffer = builder.build().unwrap();
|
||||||
|
|
||||||
|
{
|
||||||
|
let surface = window_surface.surface.as_mut().unwrap();
|
||||||
|
|
||||||
|
let future = surface
|
||||||
|
.previous_frame_end
|
||||||
|
.take()
|
||||||
|
.unwrap()
|
||||||
|
.join(acquire_future)
|
||||||
|
.then_execute(graphics_queue.0.clone(), command_buffer)
|
||||||
|
.unwrap()
|
||||||
|
.then_swapchain_present(
|
||||||
|
graphics_queue.0.clone(),
|
||||||
|
SwapchainPresentInfo::swapchain_image_index(surface.swapchain.clone(), image_index),
|
||||||
|
)
|
||||||
|
.then_signal_fence_and_flush();
|
||||||
|
|
||||||
|
match future.map_err(Validated::unwrap) {
|
||||||
|
Ok(future) => {
|
||||||
|
surface.previous_frame_end = Some(future.boxed_send_sync());
|
||||||
|
}
|
||||||
|
Err(VulkanError::OutOfDate) => {
|
||||||
|
surface.recreate_swapchain = true;
|
||||||
|
surface.previous_frame_end = Some(sync::now(device.0.clone()).boxed_send_sync());
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
println!("failed to flush future: {e}");
|
||||||
|
surface.previous_frame_end = Some(sync::now(device.0.clone()).boxed_send_sync());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn acquire_image(surface: &mut WindowSurfaceData) -> Option<(u32, SwapchainAcquireFuture)> {
|
||||||
|
surface
|
||||||
|
.previous_frame_end
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.cleanup_finished();
|
||||||
|
|
||||||
|
let (image_index, suboptimal, acquire_future) =
|
||||||
|
match acquire_next_image(surface.swapchain.clone(), None).map_err(Validated::unwrap) {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(VulkanError::OutOfDate) => {
|
||||||
|
surface.recreate_swapchain = true;
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Err(e) => panic!("failed to acquire next image: {e}"),
|
||||||
|
};
|
||||||
|
|
||||||
|
if suboptimal {
|
||||||
|
surface.recreate_swapchain = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some((image_index, acquire_future))
|
||||||
|
}
|
222
crates/engine_render/src/window/mod.rs
Normal file
222
crates/engine_render/src/window/mod.rs
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use bevy_app::{App, Plugin};
|
||||||
|
use bevy_ecs::{
|
||||||
|
resource::Resource,
|
||||||
|
schedule::IntoScheduleConfigs,
|
||||||
|
system::{Res, ResMut},
|
||||||
|
};
|
||||||
|
use engine_vulkan::{VulkanDevice, VulkanInstance};
|
||||||
|
use engine_window::raw_handle::WindowWrapper;
|
||||||
|
use vulkano::{
|
||||||
|
image::{Image, ImageUsage, view::ImageView},
|
||||||
|
pipeline::graphics::viewport::Viewport,
|
||||||
|
swapchain::{Surface, Swapchain, SwapchainCreateInfo},
|
||||||
|
sync::{self, GpuFuture},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{Render, RenderApp, RenderSystems};
|
||||||
|
|
||||||
|
pub struct WindowSurfaceData {
|
||||||
|
pub swapchain: Arc<Swapchain>,
|
||||||
|
pub attachment_image_views: Vec<Arc<ImageView>>,
|
||||||
|
pub viewport: Viewport,
|
||||||
|
pub recreate_swapchain: bool,
|
||||||
|
pub previous_frame_end: Option<Box<dyn GpuFuture + Send + Sync>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Resource, Default)]
|
||||||
|
pub struct WindowSurface {
|
||||||
|
pub surface: Option<WindowSurfaceData>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WindowRenderPlugin;
|
||||||
|
|
||||||
|
impl Plugin for WindowRenderPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
let render_app = app
|
||||||
|
.get_sub_app_mut(RenderApp)
|
||||||
|
.expect("Failed to get RenderApp. Check is RenderPlugin is added.");
|
||||||
|
|
||||||
|
render_app.init_resource::<WindowSurface>();
|
||||||
|
|
||||||
|
render_app.add_systems(
|
||||||
|
Render,
|
||||||
|
create_window_surface
|
||||||
|
.in_set(RenderSystems::ManageViews)
|
||||||
|
.run_if(need_create_window_surface)
|
||||||
|
.before(need_update_window_surface),
|
||||||
|
);
|
||||||
|
|
||||||
|
render_app.add_systems(
|
||||||
|
Render,
|
||||||
|
update_window_surface
|
||||||
|
.in_set(RenderSystems::ManageViews)
|
||||||
|
.run_if(need_update_window_surface),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn need_create_window_surface(window_surface: Res<WindowSurface>) -> bool {
|
||||||
|
window_surface.surface.is_none()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_window_surface(
|
||||||
|
mut window_surface: ResMut<WindowSurface>,
|
||||||
|
window_handle: Res<WindowWrapper>,
|
||||||
|
vulkan_instance: Res<VulkanInstance>,
|
||||||
|
vulkan_device: Res<VulkanDevice>,
|
||||||
|
) {
|
||||||
|
let window_size = window_handle.0.inner_size();
|
||||||
|
|
||||||
|
let surface = Surface::from_window(vulkan_instance.0.clone(), window_handle.0.clone())
|
||||||
|
.expect("Failed to create surface");
|
||||||
|
log::debug!("Surface created");
|
||||||
|
|
||||||
|
let (swapchain, images) = {
|
||||||
|
let surface_capabilities = vulkan_device
|
||||||
|
.0
|
||||||
|
.physical_device()
|
||||||
|
.surface_capabilities(&surface, Default::default())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let (image_format, _) = vulkan_device
|
||||||
|
.0
|
||||||
|
.physical_device()
|
||||||
|
.surface_formats(&surface, Default::default())
|
||||||
|
.unwrap()[0];
|
||||||
|
|
||||||
|
Swapchain::new(
|
||||||
|
vulkan_device.0.clone(),
|
||||||
|
surface,
|
||||||
|
SwapchainCreateInfo {
|
||||||
|
// 2 because with some graphics driver, it crash on fullscreen because fullscreen need to min image to works.
|
||||||
|
min_image_count: surface_capabilities.min_image_count.max(2),
|
||||||
|
image_format,
|
||||||
|
image_extent: window_size.into(),
|
||||||
|
image_usage: ImageUsage::COLOR_ATTACHMENT,
|
||||||
|
composite_alpha: surface_capabilities
|
||||||
|
.supported_composite_alpha
|
||||||
|
.into_iter()
|
||||||
|
.next()
|
||||||
|
.unwrap(),
|
||||||
|
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
log_swapchain_info(&swapchain, false);
|
||||||
|
|
||||||
|
let attachment_image_views = window_size_dependent_setup(&images);
|
||||||
|
|
||||||
|
let viewport = Viewport {
|
||||||
|
offset: [0.0, 0.0],
|
||||||
|
extent: window_size.into(),
|
||||||
|
depth_range: 0.0..=1.0,
|
||||||
|
};
|
||||||
|
log_viewport_info(&viewport, false);
|
||||||
|
|
||||||
|
let recreate_swapchain = false;
|
||||||
|
let previous_frame_end = Some(sync::now(vulkan_device.0.clone()).boxed_send_sync());
|
||||||
|
|
||||||
|
window_surface.surface = Some(WindowSurfaceData {
|
||||||
|
swapchain,
|
||||||
|
attachment_image_views,
|
||||||
|
viewport,
|
||||||
|
recreate_swapchain,
|
||||||
|
previous_frame_end,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn window_size_dependent_setup(images: &[Arc<Image>]) -> Vec<Arc<ImageView>> {
|
||||||
|
images
|
||||||
|
.iter()
|
||||||
|
.map(|image| ImageView::new_default(image.clone()).unwrap())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn need_update_window_surface(
|
||||||
|
window_surface: Res<WindowSurface>,
|
||||||
|
window_handle: Res<WindowWrapper>,
|
||||||
|
) -> bool {
|
||||||
|
match &window_surface.surface {
|
||||||
|
Some(surface) => {
|
||||||
|
let window_size: [f32; 2] = window_handle.0.inner_size().into();
|
||||||
|
surface.recreate_swapchain || surface.viewport.extent != window_size
|
||||||
|
}
|
||||||
|
None => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_window_surface(
|
||||||
|
mut window_surface: ResMut<WindowSurface>,
|
||||||
|
window_handle: Res<WindowWrapper>,
|
||||||
|
) {
|
||||||
|
let window_surface = window_surface.surface.as_mut().unwrap();
|
||||||
|
|
||||||
|
let window_size = window_handle.0.inner_size();
|
||||||
|
let (new_swapchain, new_images) = window_surface
|
||||||
|
.swapchain
|
||||||
|
.recreate(SwapchainCreateInfo {
|
||||||
|
image_extent: window_size.into(),
|
||||||
|
..window_surface.swapchain.create_info()
|
||||||
|
})
|
||||||
|
.expect("Failed to recreate swapchain");
|
||||||
|
|
||||||
|
window_surface.swapchain = new_swapchain;
|
||||||
|
window_surface.attachment_image_views = window_size_dependent_setup(&new_images);
|
||||||
|
window_surface.viewport.extent = window_size.into();
|
||||||
|
window_surface.recreate_swapchain = false;
|
||||||
|
|
||||||
|
log_swapchain_info(&window_surface.swapchain, true);
|
||||||
|
log_viewport_info(&window_surface.viewport, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log_swapchain_info(swapchain: &Swapchain, recreate_swapchain: bool) {
|
||||||
|
if recreate_swapchain {
|
||||||
|
log::debug!("Swapchain recreated");
|
||||||
|
} else {
|
||||||
|
log::debug!("Swapchain created");
|
||||||
|
}
|
||||||
|
log::debug!(
|
||||||
|
"\tMin image count: {}",
|
||||||
|
swapchain.create_info().min_image_count
|
||||||
|
);
|
||||||
|
log::debug!("\tImage format: {:?}", swapchain.create_info().image_format);
|
||||||
|
log::debug!("\tImage extent: {:?}", swapchain.create_info().image_extent);
|
||||||
|
log::debug!("\tImage usage: {:?}", swapchain.create_info().image_usage);
|
||||||
|
log::debug!(
|
||||||
|
"\tComposite alpha: {:?}",
|
||||||
|
swapchain.create_info().composite_alpha
|
||||||
|
);
|
||||||
|
log::debug!("\tPresent mode: {:?}", swapchain.create_info().present_mode);
|
||||||
|
log::debug!(
|
||||||
|
"\tImage sharing: {:?}",
|
||||||
|
swapchain.create_info().image_sharing
|
||||||
|
);
|
||||||
|
log::debug!(
|
||||||
|
"\tPre transform: {:?}",
|
||||||
|
swapchain.create_info().pre_transform
|
||||||
|
);
|
||||||
|
log::debug!(
|
||||||
|
"\tComposite alpha: {:?}",
|
||||||
|
swapchain.create_info().composite_alpha
|
||||||
|
);
|
||||||
|
log::debug!("\tPresent mode: {:?}", swapchain.create_info().present_mode);
|
||||||
|
log::debug!(
|
||||||
|
"\tFull screen exclusive: {:?}",
|
||||||
|
swapchain.create_info().full_screen_exclusive
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log_viewport_info(viewport: &Viewport, recreate_viewport: bool) {
|
||||||
|
if recreate_viewport {
|
||||||
|
log::debug!("Viewport recreated");
|
||||||
|
} else {
|
||||||
|
log::debug!("Viewport created");
|
||||||
|
}
|
||||||
|
log::debug!("\tOffset: {:?}", viewport.offset);
|
||||||
|
log::debug!("\tExtent: {:?}", viewport.extent);
|
||||||
|
log::debug!("\tDepth range: {:?}", viewport.depth_range);
|
||||||
|
}
|
14
crates/engine_vulkan/Cargo.toml
Normal file
14
crates/engine_vulkan/Cargo.toml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
[package]
|
||||||
|
name = "engine_vulkan"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
thiserror = { workspace = true }
|
||||||
|
log = { workspace = true }
|
||||||
|
env_logger = { workspace = true }
|
||||||
|
bevy_app = { workspace = true }
|
||||||
|
bevy_ecs = { workspace = true }
|
||||||
|
winit = { workspace = true }
|
||||||
|
vulkano = { workspace = true }
|
||||||
|
engine_window = { workspace = true }
|
214
crates/engine_vulkan/src/device.rs
Normal file
214
crates/engine_vulkan/src/device.rs
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use bevy_ecs::world::World;
|
||||||
|
use engine_window::raw_handle::DisplayHandleWrapper;
|
||||||
|
use vulkano::{
|
||||||
|
command_buffer::allocator::StandardCommandBufferAllocator,
|
||||||
|
descriptor_set::allocator::StandardDescriptorSetAllocator,
|
||||||
|
device::{
|
||||||
|
Device, DeviceCreateInfo, DeviceExtensions, Queue,
|
||||||
|
physical::{PhysicalDevice, PhysicalDeviceType},
|
||||||
|
},
|
||||||
|
memory::allocator::StandardMemoryAllocator,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
VulkanCommandBufferAllocator, VulkanComputeQueue, VulkanConfig, VulkanDescriptorSetAllocator,
|
||||||
|
VulkanDevice, VulkanGraphicsQueue, VulkanInstance, VulkanMemoryAllocator, VulkanTransferQueue,
|
||||||
|
queues::{VulkanQueueFamilyIndices, VulkanQueuesQuery, find_queues_family_indices},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn create_and_insert_device(world: &mut World, config: &VulkanConfig) {
|
||||||
|
let picked_device =
|
||||||
|
pick_physical_device(world, &config).expect("Failed to pick physical device");
|
||||||
|
|
||||||
|
let device = picked_device.device;
|
||||||
|
let physical_device = device.physical_device();
|
||||||
|
|
||||||
|
log::debug!("Vulkan device created");
|
||||||
|
log::debug!(
|
||||||
|
"\tPhysical device: {:?} ({:?})",
|
||||||
|
physical_device.properties().device_name,
|
||||||
|
physical_device.properties().device_type
|
||||||
|
);
|
||||||
|
log::debug!("\tDevice extensions: {:?}", device.enabled_extensions());
|
||||||
|
log::debug!("\tDevice features: {:?}", device.enabled_features());
|
||||||
|
|
||||||
|
world.insert_resource(VulkanDevice(device.clone()));
|
||||||
|
|
||||||
|
log::debug!("\tDevice selected queues:");
|
||||||
|
if config.with_graphics_queue {
|
||||||
|
world.insert_resource(VulkanGraphicsQueue(
|
||||||
|
picked_device
|
||||||
|
.graphics_queue
|
||||||
|
.expect("Failed to get graphics queue"),
|
||||||
|
));
|
||||||
|
log::debug!("\t\t- Graphics queue");
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.with_compute_queue {
|
||||||
|
world.insert_resource(VulkanComputeQueue(
|
||||||
|
picked_device
|
||||||
|
.compute_queue
|
||||||
|
.expect("Failed to get compute queue"),
|
||||||
|
));
|
||||||
|
log::debug!("\t\t- Compute queue");
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.with_transfer_queue {
|
||||||
|
world.insert_resource(VulkanTransferQueue(
|
||||||
|
picked_device
|
||||||
|
.transfer_queue
|
||||||
|
.expect("Failed to get transfer queue"),
|
||||||
|
));
|
||||||
|
log::debug!("\t\t- Transfer queue");
|
||||||
|
}
|
||||||
|
|
||||||
|
world.insert_resource(VulkanMemoryAllocator(Arc::new(
|
||||||
|
StandardMemoryAllocator::new_default(device.clone()),
|
||||||
|
)));
|
||||||
|
world.insert_resource(VulkanCommandBufferAllocator(Arc::new(
|
||||||
|
StandardCommandBufferAllocator::new(device.clone(), Default::default()),
|
||||||
|
)));
|
||||||
|
world.insert_resource(VulkanDescriptorSetAllocator(Arc::new(
|
||||||
|
StandardDescriptorSetAllocator::new(device.clone(), Default::default()),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PickedDevice {
|
||||||
|
pub device: Arc<Device>,
|
||||||
|
pub graphics_queue: Option<Arc<Queue>>,
|
||||||
|
pub compute_queue: Option<Arc<Queue>>,
|
||||||
|
pub transfer_queue: Option<Arc<Queue>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pick_physical_device(world: &World, config: &VulkanConfig) -> Option<PickedDevice> {
|
||||||
|
let instance = world
|
||||||
|
.get_resource::<VulkanInstance>()
|
||||||
|
.expect("Failed to get VulkanInstance during vulkan plugin initialization");
|
||||||
|
|
||||||
|
instance
|
||||||
|
.0
|
||||||
|
.enumerate_physical_devices()
|
||||||
|
.expect("Failed to enumerate physical devices")
|
||||||
|
.filter_map(|p| check_physical_device_support(world, &p, config).and_then(|r| Some((p, r))))
|
||||||
|
.min_by_key(|(p, _)| match p.properties().device_type {
|
||||||
|
PhysicalDeviceType::DiscreteGpu => 0,
|
||||||
|
PhysicalDeviceType::IntegratedGpu => 1,
|
||||||
|
PhysicalDeviceType::VirtualGpu => 2,
|
||||||
|
PhysicalDeviceType::Cpu => 3,
|
||||||
|
PhysicalDeviceType::Other => 4,
|
||||||
|
_ => 5,
|
||||||
|
})
|
||||||
|
.take()
|
||||||
|
.and_then(|(p, (device_extensions, queue_family_indices))| {
|
||||||
|
Some(create_device(
|
||||||
|
config,
|
||||||
|
&p,
|
||||||
|
device_extensions,
|
||||||
|
queue_family_indices,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_device_extensions_support(
|
||||||
|
physical_device: &Arc<PhysicalDevice>,
|
||||||
|
config: &VulkanConfig,
|
||||||
|
) -> Option<DeviceExtensions> {
|
||||||
|
let device_extensions = DeviceExtensions {
|
||||||
|
khr_swapchain: config.with_window_surface,
|
||||||
|
..config.device_extensions
|
||||||
|
};
|
||||||
|
|
||||||
|
if physical_device
|
||||||
|
.supported_extensions()
|
||||||
|
.contains(&device_extensions)
|
||||||
|
{
|
||||||
|
log::debug!(
|
||||||
|
"\t\t[OK] Device supports required extensions {:?}",
|
||||||
|
device_extensions
|
||||||
|
);
|
||||||
|
Some(device_extensions)
|
||||||
|
} else {
|
||||||
|
log::debug!(
|
||||||
|
"\t\t[FAILED] Device does not support required extensions {:?}",
|
||||||
|
device_extensions
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_physical_device_support(
|
||||||
|
world: &World,
|
||||||
|
physical_device: &Arc<PhysicalDevice>,
|
||||||
|
config: &VulkanConfig,
|
||||||
|
) -> Option<(DeviceExtensions, VulkanQueueFamilyIndices)> {
|
||||||
|
log::debug!("Checking physical device");
|
||||||
|
log::debug!("\tProperties");
|
||||||
|
log::debug!("\t\tName: {}", physical_device.properties().device_name);
|
||||||
|
log::debug!("\t\tAPI version: {}", physical_device.api_version());
|
||||||
|
log::debug!(
|
||||||
|
"\t\tDevice type: {:?}",
|
||||||
|
physical_device.properties().device_type
|
||||||
|
);
|
||||||
|
log::debug!("\tRequired supports checking report");
|
||||||
|
|
||||||
|
let device_extensions = check_device_extensions_support(physical_device, config)?;
|
||||||
|
|
||||||
|
let display_handle = world
|
||||||
|
.get_resource::<DisplayHandleWrapper>()
|
||||||
|
.expect("DisplayHandleWrapper must be added before VulkanPlugin");
|
||||||
|
|
||||||
|
let queue_support_query = VulkanQueuesQuery {
|
||||||
|
with_surface: Some(&display_handle),
|
||||||
|
with_graphics_queue: config.with_graphics_queue,
|
||||||
|
with_compute_queue: config.with_compute_queue,
|
||||||
|
with_transfer_queue: config.with_transfer_queue,
|
||||||
|
};
|
||||||
|
|
||||||
|
let queue_family_indices = find_queues_family_indices(physical_device, &queue_support_query);
|
||||||
|
log::debug!("\t\tQueue family indices: {:#?}", queue_family_indices);
|
||||||
|
|
||||||
|
Some((device_extensions, queue_family_indices))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_device(
|
||||||
|
config: &VulkanConfig,
|
||||||
|
physical_device: &Arc<PhysicalDevice>,
|
||||||
|
device_extensions: DeviceExtensions,
|
||||||
|
queue_family_indices: VulkanQueueFamilyIndices,
|
||||||
|
) -> PickedDevice {
|
||||||
|
let (device, mut queues) = Device::new(
|
||||||
|
physical_device.clone(),
|
||||||
|
DeviceCreateInfo {
|
||||||
|
queue_create_infos: queue_family_indices.into(),
|
||||||
|
enabled_extensions: device_extensions,
|
||||||
|
enabled_features: config.device_features,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.expect("Failed to create device");
|
||||||
|
|
||||||
|
let mut graphics_queue = None;
|
||||||
|
let mut compute_queue = None;
|
||||||
|
let mut transfer_queue = None;
|
||||||
|
|
||||||
|
if config.with_graphics_queue {
|
||||||
|
graphics_queue = queues.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.with_compute_queue {
|
||||||
|
compute_queue = queues.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.with_transfer_queue {
|
||||||
|
transfer_queue = queues.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
PickedDevice {
|
||||||
|
device,
|
||||||
|
graphics_queue,
|
||||||
|
compute_queue,
|
||||||
|
transfer_queue,
|
||||||
|
}
|
||||||
|
}
|
70
crates/engine_vulkan/src/instance.rs
Normal file
70
crates/engine_vulkan/src/instance.rs
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use bevy_ecs::world::World;
|
||||||
|
use engine_window::raw_handle::DisplayHandleWrapper;
|
||||||
|
use vulkano::{
|
||||||
|
VulkanLibrary,
|
||||||
|
instance::{Instance, InstanceCreateFlags, InstanceCreateInfo, InstanceExtensions},
|
||||||
|
swapchain::Surface,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{VulkanConfig, VulkanInstance};
|
||||||
|
|
||||||
|
fn load_library() -> Arc<VulkanLibrary> {
|
||||||
|
let library = VulkanLibrary::new().unwrap();
|
||||||
|
|
||||||
|
log::debug!("Available Instance layers:");
|
||||||
|
for layer in library.layer_properties().unwrap() {
|
||||||
|
log::debug!(
|
||||||
|
"\t - Layer name: {}, Description: {}, Implementation Version: {}, Vulkan Version: {}",
|
||||||
|
layer.name(),
|
||||||
|
layer.description(),
|
||||||
|
layer.implementation_version(),
|
||||||
|
layer.vulkan_version()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
library
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_and_insert_instance(world: &mut World, config: &VulkanConfig) {
|
||||||
|
let library = load_library();
|
||||||
|
|
||||||
|
let instance_extensions = {
|
||||||
|
if config.with_window_surface {
|
||||||
|
let display_handle = world
|
||||||
|
.get_resource::<DisplayHandleWrapper>()
|
||||||
|
.expect("DisplayHandleWrapper must be added before VulkanPlugin");
|
||||||
|
|
||||||
|
Surface::required_extensions(&display_handle.0)
|
||||||
|
.expect("Failed to get surface required extensions")
|
||||||
|
} else {
|
||||||
|
InstanceExtensions::default()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let instance = Instance::new(
|
||||||
|
library,
|
||||||
|
InstanceCreateInfo {
|
||||||
|
// Enable enumerating devices that use non-conformant Vulkan implementations.
|
||||||
|
// (e.g. MoltenVK)
|
||||||
|
flags: InstanceCreateFlags::ENUMERATE_PORTABILITY,
|
||||||
|
enabled_extensions: instance_extensions,
|
||||||
|
enabled_layers: config.instance_layers.clone(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.expect("Failed to create vulkan instance");
|
||||||
|
|
||||||
|
log::debug!("Instance created");
|
||||||
|
log::debug!(
|
||||||
|
"\t- Enabled extensions: {:?}",
|
||||||
|
instance.enabled_extensions()
|
||||||
|
);
|
||||||
|
log::debug!("\t- Enabled layers: {:?}", instance.enabled_layers());
|
||||||
|
log::debug!("\t- API version: {:?}", instance.api_version());
|
||||||
|
log::debug!("\t- Max API version: {:?}", instance.max_api_version());
|
||||||
|
log::debug!("\t- Flags: {:?}", instance.flags());
|
||||||
|
|
||||||
|
world.insert_resource(VulkanInstance(instance));
|
||||||
|
}
|
85
crates/engine_vulkan/src/lib.rs
Normal file
85
crates/engine_vulkan/src/lib.rs
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use bevy_app::{App, Plugin};
|
||||||
|
use bevy_ecs::resource::Resource;
|
||||||
|
use vulkano::{
|
||||||
|
command_buffer::allocator::StandardCommandBufferAllocator,
|
||||||
|
descriptor_set::allocator::StandardDescriptorSetAllocator,
|
||||||
|
device::{Device, DeviceExtensions, DeviceFeatures, Queue},
|
||||||
|
instance::Instance,
|
||||||
|
memory::allocator::StandardMemoryAllocator,
|
||||||
|
};
|
||||||
|
|
||||||
|
mod device;
|
||||||
|
mod instance;
|
||||||
|
mod queues;
|
||||||
|
|
||||||
|
use crate::{device::create_and_insert_device, instance::create_and_insert_instance};
|
||||||
|
|
||||||
|
#[derive(Resource, Clone)]
|
||||||
|
pub struct VulkanInstance(pub Arc<Instance>);
|
||||||
|
|
||||||
|
#[derive(Resource, Clone)]
|
||||||
|
pub struct VulkanDevice(pub Arc<Device>);
|
||||||
|
|
||||||
|
#[derive(Resource, Clone)]
|
||||||
|
pub struct VulkanGraphicsQueue(pub Arc<Queue>);
|
||||||
|
|
||||||
|
#[derive(Resource, Clone)]
|
||||||
|
pub struct VulkanComputeQueue(pub Arc<Queue>);
|
||||||
|
|
||||||
|
#[derive(Resource, Clone)]
|
||||||
|
pub struct VulkanTransferQueue(pub Arc<Queue>);
|
||||||
|
|
||||||
|
#[derive(Resource, Clone)]
|
||||||
|
pub struct VulkanMemoryAllocator(pub Arc<StandardMemoryAllocator>);
|
||||||
|
|
||||||
|
#[derive(Resource, Clone)]
|
||||||
|
pub struct VulkanCommandBufferAllocator(pub Arc<StandardCommandBufferAllocator>);
|
||||||
|
|
||||||
|
#[derive(Resource, Clone)]
|
||||||
|
pub struct VulkanDescriptorSetAllocator(pub Arc<StandardDescriptorSetAllocator>);
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum VulkanError {
|
||||||
|
#[error("Failed to create vulkan context")]
|
||||||
|
FailedToCreateVulkanContext,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct VulkanConfig {
|
||||||
|
pub instance_layers: Vec<String>,
|
||||||
|
pub device_extensions: DeviceExtensions,
|
||||||
|
pub device_features: DeviceFeatures,
|
||||||
|
pub with_window_surface: bool,
|
||||||
|
pub with_graphics_queue: bool,
|
||||||
|
pub with_compute_queue: bool,
|
||||||
|
pub with_transfer_queue: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for VulkanConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
instance_layers: Vec::default(),
|
||||||
|
device_extensions: DeviceExtensions::default(),
|
||||||
|
device_features: DeviceFeatures::default(),
|
||||||
|
with_window_surface: true,
|
||||||
|
with_graphics_queue: true,
|
||||||
|
with_compute_queue: true,
|
||||||
|
with_transfer_queue: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct VulkanPlugin {
|
||||||
|
pub vulkan_config: VulkanConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Plugin for VulkanPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
let world = app.world_mut();
|
||||||
|
|
||||||
|
create_and_insert_instance(world, &self.vulkan_config);
|
||||||
|
create_and_insert_device(world, &self.vulkan_config);
|
||||||
|
}
|
||||||
|
}
|
176
crates/engine_vulkan/src/queues.rs
Normal file
176
crates/engine_vulkan/src/queues.rs
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
use std::{collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
|
use engine_window::raw_handle::DisplayHandleWrapper;
|
||||||
|
use vulkano::device::{
|
||||||
|
QueueCreateInfo, QueueFamilyProperties, QueueFlags, physical::PhysicalDevice,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum VulkanQueueFamilyStatus {
|
||||||
|
Unused,
|
||||||
|
NotSupported,
|
||||||
|
Supported(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VulkanQueueFamilyStatus {
|
||||||
|
pub fn can_be_set(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
VulkanQueueFamilyStatus::NotSupported => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert a boolean to a VulkanQueueFamilyStatus as default value
|
||||||
|
impl From<bool> for VulkanQueueFamilyStatus {
|
||||||
|
fn from(value: bool) -> Self {
|
||||||
|
if value {
|
||||||
|
VulkanQueueFamilyStatus::NotSupported
|
||||||
|
} else {
|
||||||
|
VulkanQueueFamilyStatus::Unused
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert a DisplayHandleWrapper Option to a VulkanQueueFamilyStatus as default value
|
||||||
|
impl From<Option<&DisplayHandleWrapper>> for VulkanQueueFamilyStatus {
|
||||||
|
fn from(value: Option<&DisplayHandleWrapper>) -> Self {
|
||||||
|
if value.is_some() {
|
||||||
|
VulkanQueueFamilyStatus::NotSupported
|
||||||
|
} else {
|
||||||
|
VulkanQueueFamilyStatus::Unused
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct VulkanQueueFamilyIndices {
|
||||||
|
pub graphics_queue_family_index: VulkanQueueFamilyStatus,
|
||||||
|
pub compute_queue_family_index: VulkanQueueFamilyStatus,
|
||||||
|
pub transfer_queue_family_index: VulkanQueueFamilyStatus,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<VulkanQueueFamilyIndices> for Vec<(VulkanQueueFamilyStatus, f32)> {
|
||||||
|
fn from(indices: VulkanQueueFamilyIndices) -> Self {
|
||||||
|
vec![
|
||||||
|
(indices.graphics_queue_family_index, 1.0),
|
||||||
|
(indices.compute_queue_family_index, 0.5),
|
||||||
|
(indices.transfer_queue_family_index, 0.5),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<VulkanQueueFamilyIndices> for Vec<QueueCreateInfo> {
|
||||||
|
fn from(indices: VulkanQueueFamilyIndices) -> Self {
|
||||||
|
let mut queue_create_infos = HashMap::<u32, QueueCreateInfo>::new();
|
||||||
|
|
||||||
|
let statuses: Vec<(VulkanQueueFamilyStatus, f32)> = indices.into();
|
||||||
|
|
||||||
|
for (status, priority) in statuses.iter() {
|
||||||
|
match status {
|
||||||
|
VulkanQueueFamilyStatus::Supported(index) => {
|
||||||
|
let entry = queue_create_infos.entry(*index).or_insert(QueueCreateInfo {
|
||||||
|
queue_family_index: *index,
|
||||||
|
queues: Vec::new(),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
entry.queues.push(*priority);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
queue_create_infos
|
||||||
|
.into_iter()
|
||||||
|
.map(|(_, value)| value)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert a VulkanQueuesQuery to a VulkanQueuesFamilyIndices as default value
|
||||||
|
impl<'a> From<&'a VulkanQueuesQuery<'a>> for VulkanQueueFamilyIndices {
|
||||||
|
fn from(query: &'a VulkanQueuesQuery<'a>) -> Self {
|
||||||
|
VulkanQueueFamilyIndices {
|
||||||
|
graphics_queue_family_index: query.with_graphics_queue.into(),
|
||||||
|
compute_queue_family_index: query.with_compute_queue.into(),
|
||||||
|
transfer_queue_family_index: query.with_transfer_queue.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct VulkanQueuesQuery<'a> {
|
||||||
|
pub with_surface: Option<&'a DisplayHandleWrapper>,
|
||||||
|
pub with_graphics_queue: bool,
|
||||||
|
pub with_compute_queue: bool,
|
||||||
|
pub with_transfer_queue: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_queues_family_indices(
|
||||||
|
physical_device: &Arc<PhysicalDevice>,
|
||||||
|
query: &VulkanQueuesQuery,
|
||||||
|
) -> VulkanQueueFamilyIndices {
|
||||||
|
let mut indices: VulkanQueueFamilyIndices = query.into();
|
||||||
|
|
||||||
|
for (i, queue_family_properties) in physical_device.queue_family_properties().iter().enumerate()
|
||||||
|
{
|
||||||
|
let mut available_queue_count = queue_family_properties.queue_count;
|
||||||
|
|
||||||
|
if indices.graphics_queue_family_index.can_be_set()
|
||||||
|
&& check_queue_support(queue_family_properties, QueueFlags::GRAPHICS)
|
||||||
|
&& available_queue_count > 0
|
||||||
|
{
|
||||||
|
if query.with_surface.is_none()
|
||||||
|
|| check_presentation_support(
|
||||||
|
physical_device,
|
||||||
|
i as u32,
|
||||||
|
&query.with_surface.as_ref().unwrap(),
|
||||||
|
)
|
||||||
|
{
|
||||||
|
indices.graphics_queue_family_index = VulkanQueueFamilyStatus::Supported(i as u32);
|
||||||
|
available_queue_count -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if indices.compute_queue_family_index.can_be_set()
|
||||||
|
&& check_queue_support(queue_family_properties, QueueFlags::COMPUTE)
|
||||||
|
&& available_queue_count > 0
|
||||||
|
{
|
||||||
|
indices.compute_queue_family_index = VulkanQueueFamilyStatus::Supported(i as u32);
|
||||||
|
available_queue_count -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if indices.transfer_queue_family_index.can_be_set()
|
||||||
|
&& check_queue_support(queue_family_properties, QueueFlags::TRANSFER)
|
||||||
|
&& available_queue_count > 0
|
||||||
|
{
|
||||||
|
indices.transfer_queue_family_index = VulkanQueueFamilyStatus::Supported(i as u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !indices.graphics_queue_family_index.can_be_set()
|
||||||
|
&& !indices.compute_queue_family_index.can_be_set()
|
||||||
|
&& !indices.transfer_queue_family_index.can_be_set()
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return indices;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_presentation_support(
|
||||||
|
physical_device: &Arc<PhysicalDevice>,
|
||||||
|
queue_family_index: u32,
|
||||||
|
surface: &DisplayHandleWrapper,
|
||||||
|
) -> bool {
|
||||||
|
physical_device
|
||||||
|
.presentation_support(queue_family_index, &surface.0)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_queue_support(
|
||||||
|
queue_family_property: &QueueFamilyProperties,
|
||||||
|
queue_flags: QueueFlags,
|
||||||
|
) -> bool {
|
||||||
|
queue_family_property.queue_flags.intersects(queue_flags)
|
||||||
|
}
|
11
crates/engine_window/Cargo.toml
Normal file
11
crates/engine_window/Cargo.toml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
[package]
|
||||||
|
name = "engine_window"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
thiserror = { workspace = true }
|
||||||
|
log = { workspace = true }
|
||||||
|
bevy_app = { workspace = true }
|
||||||
|
bevy_ecs = { workspace = true }
|
||||||
|
winit = { workspace = true }
|
17
crates/engine_window/src/config.rs
Normal file
17
crates/engine_window/src/config.rs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
use bevy_ecs::resource::Resource;
|
||||||
|
use winit::{dpi::PhysicalSize, window::WindowAttributes};
|
||||||
|
|
||||||
|
#[derive(Resource, Clone)]
|
||||||
|
pub struct WindowConfig {
|
||||||
|
pub title: String,
|
||||||
|
pub width: u32,
|
||||||
|
pub height: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<WindowAttributes> for &WindowConfig {
|
||||||
|
fn into(self) -> WindowAttributes {
|
||||||
|
WindowAttributes::default()
|
||||||
|
.with_title(self.title.clone())
|
||||||
|
.with_inner_size(PhysicalSize::new(self.width as f64, self.height as f64))
|
||||||
|
}
|
||||||
|
}
|
56
crates/engine_window/src/lib.rs
Normal file
56
crates/engine_window/src/lib.rs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
use bevy_app::{App, AppExit, Plugin, PluginsState};
|
||||||
|
use config::WindowConfig;
|
||||||
|
use raw_handle::{DisplayHandleWrapper, EventLoopProxyWrapper, WindowWrapper};
|
||||||
|
use state::WindowState;
|
||||||
|
use winit::event_loop::EventLoop;
|
||||||
|
|
||||||
|
pub mod config;
|
||||||
|
pub mod raw_handle;
|
||||||
|
pub mod state;
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum WindowError {
|
||||||
|
#[error("Failed to create event loop")]
|
||||||
|
FailedToCreateEventLoop,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WindowPlugin {
|
||||||
|
pub window_config: WindowConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Plugin for WindowPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
let world = app.world_mut();
|
||||||
|
world.insert_resource(self.window_config.clone());
|
||||||
|
|
||||||
|
let mut event_loop_builder = EventLoop::with_user_event();
|
||||||
|
let event_loop = event_loop_builder
|
||||||
|
.build()
|
||||||
|
.map_err(|_| WindowError::FailedToCreateEventLoop)
|
||||||
|
.expect("Failed to create event loop");
|
||||||
|
|
||||||
|
world.insert_resource(DisplayHandleWrapper(event_loop.owned_display_handle()));
|
||||||
|
|
||||||
|
app.set_runner(Box::new(move |app| runner(app, event_loop)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn runner(mut app: App, event_loop: EventLoop<()>) -> AppExit {
|
||||||
|
if app.plugins_state() == PluginsState::Ready {
|
||||||
|
app.finish();
|
||||||
|
app.cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.world_mut()
|
||||||
|
.insert_resource(EventLoopProxyWrapper::new(event_loop.create_proxy()));
|
||||||
|
|
||||||
|
let mut window_state = WindowState::new(app);
|
||||||
|
|
||||||
|
match event_loop.run_app(&mut window_state) {
|
||||||
|
Ok(_) => AppExit::Success,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Error running window state: {e}");
|
||||||
|
AppExit::error()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
crates/engine_window/src/raw_handle.rs
Normal file
23
crates/engine_window/src/raw_handle.rs
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use bevy_ecs::resource::Resource;
|
||||||
|
use winit::{event_loop::EventLoopProxy, window::Window};
|
||||||
|
|
||||||
|
#[derive(Resource)]
|
||||||
|
pub struct EventLoopProxyWrapper<T: 'static>(EventLoopProxy<T>);
|
||||||
|
|
||||||
|
impl<T: 'static> EventLoopProxyWrapper<T> {
|
||||||
|
pub fn new(event_loop: EventLoopProxy<T>) -> Self {
|
||||||
|
Self(event_loop)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn proxy(&self) -> &EventLoopProxy<T> {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Resource, Clone)]
|
||||||
|
pub struct DisplayHandleWrapper(pub winit::event_loop::OwnedDisplayHandle);
|
||||||
|
|
||||||
|
#[derive(Resource, Clone)]
|
||||||
|
pub struct WindowWrapper(pub Arc<Window>);
|
63
crates/engine_window/src/state.rs
Normal file
63
crates/engine_window/src/state.rs
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use bevy_app::{App, PluginsState};
|
||||||
|
use bevy_ecs::world::World;
|
||||||
|
use winit::{
|
||||||
|
application::ApplicationHandler, event::WindowEvent, event_loop::ActiveEventLoop,
|
||||||
|
window::WindowId,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{config::WindowConfig, raw_handle::WindowWrapper};
|
||||||
|
|
||||||
|
pub struct WindowState {
|
||||||
|
app: App,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowState {
|
||||||
|
pub fn new(app: App) -> Self {
|
||||||
|
Self { app }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn world(&self) -> &World {
|
||||||
|
self.app.world()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApplicationHandler for WindowState {
|
||||||
|
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
|
let window_config = self.world().get_resource::<WindowConfig>().unwrap();
|
||||||
|
|
||||||
|
let window = event_loop.create_window(window_config.into()).unwrap();
|
||||||
|
self.app
|
||||||
|
.world_mut()
|
||||||
|
.insert_resource(WindowWrapper(Arc::new(window)));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: winit::event::StartCause) {
|
||||||
|
if self.app.plugins_state() == PluginsState::Ready {
|
||||||
|
self.app.finish();
|
||||||
|
self.app.cleanup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) {
|
||||||
|
match event {
|
||||||
|
WindowEvent::CloseRequested => {
|
||||||
|
log::debug!("The close button was pressed; stopping");
|
||||||
|
event_loop.exit();
|
||||||
|
}
|
||||||
|
WindowEvent::RedrawRequested => {
|
||||||
|
if self.app.plugins_state() == PluginsState::Cleaned {
|
||||||
|
self.app.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
let window_wrapper = self.app.world().get_resource::<WindowWrapper>().unwrap();
|
||||||
|
|
||||||
|
window_wrapper.0.request_redraw();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {}
|
||||||
|
}
|
|
@ -1,11 +0,0 @@
|
||||||
# Difference between Vulkan and OpenGL
|
|
||||||
|
|
||||||
Viewport:
|
|
||||||
|
|
||||||
- Y axis is flipped like D3D
|
|
||||||
- Clipped Z axis is not [-1; 1] but [0; 1]
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
See: [Vulkan Tutorial (Vertex step)](https://vulkan-tutorial.com/Drawing_a_triangle/Graphics_pipeline_basics/Shader_modules) and [VK_KHR_maintenance1 (Allow negative height)](https://registry.khronos.org/vulkan/specs/latest/man/html/VK_KHR_maintenance1.html#_description)
|
|
|
@ -1,15 +0,0 @@
|
||||||
# Informations
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let fns = vulkano_context.instance().fns();
|
|
||||||
let mut props = ash::vk::PhysicalDeviceProperties::default();
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
(fns.v1_0.get_physical_device_properties)(
|
|
||||||
vulkano_context.device().physical_device().handle(),
|
|
||||||
&mut props,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("{:?}", props);
|
|
||||||
```
|
|
Binary file not shown.
Before Width: | Height: | Size: 24 KiB |
|
@ -1,219 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="168.23553mm"
|
|
||||||
height="76.127022mm"
|
|
||||||
viewBox="0 0 596.11016 269.74141"
|
|
||||||
id="svg2"
|
|
||||||
version="1.1"
|
|
||||||
inkscape:version="0.91 r13725"
|
|
||||||
sodipodi:docname="clip_coordinates.svg">
|
|
||||||
<defs
|
|
||||||
id="defs4" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="base"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:zoom="0.98994949"
|
|
||||||
inkscape:cx="140.75091"
|
|
||||||
inkscape:cy="-3.0732866"
|
|
||||||
inkscape:document-units="px"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
showgrid="false"
|
|
||||||
inkscape:window-width="1600"
|
|
||||||
inkscape:window-height="837"
|
|
||||||
inkscape:window-x="-8"
|
|
||||||
inkscape:window-y="-8"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
fit-margin-top="10"
|
|
||||||
fit-margin-left="10"
|
|
||||||
fit-margin-right="10"
|
|
||||||
fit-margin-bottom="10" />
|
|
||||||
<metadata
|
|
||||||
id="metadata7">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
inkscape:label="Layer 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1"
|
|
||||||
transform="translate(-68.169789,-67.73013)">
|
|
||||||
<rect
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:2.37949777;stroke-opacity:1"
|
|
||||||
id="rect4136"
|
|
||||||
width="185.26089"
|
|
||||||
height="129.17273"
|
|
||||||
x="127.66544"
|
|
||||||
y="152.46893" />
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
x="142.5"
|
|
||||||
y="114.50506"
|
|
||||||
id="text4153"
|
|
||||||
sodipodi:linespacing="125%"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
id="tspan4155"
|
|
||||||
x="142.5"
|
|
||||||
y="114.50506">Framebuffer coordinates</tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
x="108.08633"
|
|
||||||
y="144.23506"
|
|
||||||
id="text4157"
|
|
||||||
sodipodi:linespacing="125%"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
id="tspan4159"
|
|
||||||
x="108.08633"
|
|
||||||
y="144.23506">(0, 0)</tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
x="289.4823"
|
|
||||||
y="143.68567"
|
|
||||||
id="text4157-1"
|
|
||||||
sodipodi:linespacing="125%"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
id="tspan4159-7"
|
|
||||||
x="289.4823"
|
|
||||||
y="143.68567">(1920, 0)</tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
x="102.49812"
|
|
||||||
y="299.52383"
|
|
||||||
id="text4157-0"
|
|
||||||
sodipodi:linespacing="125%"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
id="tspan4159-3"
|
|
||||||
x="102.49812"
|
|
||||||
y="299.52383">(0, 1080)</tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
x="277.83316"
|
|
||||||
y="298.46939"
|
|
||||||
id="text4157-1-3"
|
|
||||||
sodipodi:linespacing="125%"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
id="tspan4159-7-2"
|
|
||||||
x="277.83316"
|
|
||||||
y="298.46939">(1920, 1080)</tspan></text>
|
|
||||||
<circle
|
|
||||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-opacity:1"
|
|
||||||
id="path4229"
|
|
||||||
cx="220.46579"
|
|
||||||
cy="218.48128"
|
|
||||||
r="1.767767" />
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
x="187.29964"
|
|
||||||
y="232.99626"
|
|
||||||
id="text4157-1-3-3"
|
|
||||||
sodipodi:linespacing="125%"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
id="tspan4159-7-2-3"
|
|
||||||
x="187.29964"
|
|
||||||
y="232.99626">(960, 540)</tspan></text>
|
|
||||||
<rect
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:2.37949777;stroke-opacity:1"
|
|
||||||
id="rect4136-0"
|
|
||||||
width="185.26089"
|
|
||||||
height="129.17273"
|
|
||||||
x="426.228"
|
|
||||||
y="150.62413" />
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
x="465.34827"
|
|
||||||
y="112.66027"
|
|
||||||
id="text4153-2"
|
|
||||||
sodipodi:linespacing="125%"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
id="tspan4155-2"
|
|
||||||
x="435.34827"
|
|
||||||
y="112.66027">Normalized device coordinates</tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
x="406.6489"
|
|
||||||
y="142.39026"
|
|
||||||
id="text4157-9"
|
|
||||||
sodipodi:linespacing="125%"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
id="tspan4159-0"
|
|
||||||
x="406.6489"
|
|
||||||
y="142.39026">(-1, -1)</tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
x="588.04486"
|
|
||||||
y="141.84087"
|
|
||||||
id="text4157-1-4"
|
|
||||||
sodipodi:linespacing="125%"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
id="tspan4159-7-21"
|
|
||||||
x="588.04486"
|
|
||||||
y="141.84087">(1, -1)</tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
x="401.0607"
|
|
||||||
y="297.67902"
|
|
||||||
id="text4157-0-6"
|
|
||||||
sodipodi:linespacing="125%"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
id="tspan4159-3-5"
|
|
||||||
x="401.0607"
|
|
||||||
y="297.67902">(-1, 1)</tspan></text>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
x="592.82428"
|
|
||||||
y="296.62457"
|
|
||||||
id="text4157-1-3-7"
|
|
||||||
sodipodi:linespacing="125%"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
id="tspan4159-7-2-6"
|
|
||||||
x="592.82428"
|
|
||||||
y="296.62457">(1, 1)</tspan></text>
|
|
||||||
<circle
|
|
||||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-opacity:1"
|
|
||||||
id="path4229-5"
|
|
||||||
cx="519.02832"
|
|
||||||
cy="216.63647"
|
|
||||||
r="1.767767" />
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#4d4d4d;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
x="500.14792"
|
|
||||||
y="231.15146"
|
|
||||||
id="text4157-1-3-3-8"
|
|
||||||
sodipodi:linespacing="125%"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
id="tspan4159-7-2-3-0"
|
|
||||||
x="500.14792"
|
|
||||||
y="231.15146">(0, 0)</tspan></text>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 8.8 KiB |
|
@ -31,7 +31,7 @@
|
||||||
cargo = rust;
|
cargo = rust;
|
||||||
});
|
});
|
||||||
|
|
||||||
buildInputs = with pkgs; [ vulkan-headers vulkan-loader vulkan-validation-layers renderdoc tracy ]
|
buildInputs = with pkgs; [ vulkan-headers vulkan-loader vulkan-validation-layers renderdoc ]
|
||||||
++ pkgs.lib.optionals pkgs.stdenv.hostPlatform.isLinux (with pkgs; [
|
++ pkgs.lib.optionals pkgs.stdenv.hostPlatform.isLinux (with pkgs; [
|
||||||
stdenv.cc.cc.lib
|
stdenv.cc.cc.lib
|
||||||
|
|
||||||
|
@ -39,7 +39,6 @@
|
||||||
libxkbcommon
|
libxkbcommon
|
||||||
wayland
|
wayland
|
||||||
libGL
|
libGL
|
||||||
|
|
||||||
# Xorg
|
# Xorg
|
||||||
xorg.libX11
|
xorg.libX11
|
||||||
xorg.libXcursor
|
xorg.libXcursor
|
||||||
|
@ -65,7 +64,7 @@
|
||||||
++ packages;
|
++ packages;
|
||||||
|
|
||||||
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs;
|
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs;
|
||||||
VK_LAYER_PATH = "${pkgs.vulkan-validation-layers}/share/vulkan/explicit_layer.d";
|
VK_LAYER_PATH = "${pkgs.vulkan-validation-layers}/share/vulkan/explicit_layer.d:${pkgs.renderdoc}/share/vulkan/implicit_layer.d";
|
||||||
RUST_LOG = "debug,rust_vulkan_test=trace";
|
RUST_LOG = "debug,rust_vulkan_test=trace";
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
|
@ -77,7 +76,7 @@
|
||||||
|
|
||||||
packages = {
|
packages = {
|
||||||
default = rustPlatform.buildRustPackage {
|
default = rustPlatform.buildRustPackage {
|
||||||
pname = "vulkan_test";
|
pname = "rust_ash_test";
|
||||||
version = "0.1.0";
|
version = "0.1.0";
|
||||||
|
|
||||||
src = self;
|
src = self;
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 25 KiB |
Binary file not shown.
Before Width: | Height: | Size: 117 KiB |
1143
res/objects/cube.obj
1143
res/objects/cube.obj
File diff suppressed because it is too large
Load diff
|
@ -1,12 +0,0 @@
|
||||||
#version 450
|
|
||||||
|
|
||||||
layout (location = 0) in vec2 tex_coords;
|
|
||||||
|
|
||||||
layout (location = 0) out vec4 f_color;
|
|
||||||
|
|
||||||
layout(set = 1, binding = 0) uniform sampler mySampler;
|
|
||||||
layout(set = 1, binding = 1) uniform texture2D myTexture;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
f_color = texture(sampler2D(myTexture, mySampler), tex_coords);
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
#version 450
|
|
||||||
|
|
||||||
layout (location = 0) in vec3 position;
|
|
||||||
layout (location = 1) in vec2 uv;
|
|
||||||
layout (location = 2) in mat4 model;
|
|
||||||
|
|
||||||
layout (location = 0) out vec2 fragUv;
|
|
||||||
|
|
||||||
layout (set = 0, binding = 0) uniform MVP {
|
|
||||||
mat4 world;
|
|
||||||
mat4 view;
|
|
||||||
mat4 projection;
|
|
||||||
} uniforms;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
mat4 worldview = uniforms.view * uniforms.world;
|
|
||||||
vec4 modelPosition = model * vec4(position, 1.0);
|
|
||||||
gl_Position = uniforms.projection * worldview * modelPosition;
|
|
||||||
fragUv = uv;
|
|
||||||
}
|
|
9
res/shaders/vertex.frag
Normal file
9
res/shaders/vertex.frag
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#version 450
|
||||||
|
|
||||||
|
layout (location = 0) in vec3 color;
|
||||||
|
|
||||||
|
layout (location = 0) out vec4 f_color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
f_color = vec4(color, 1.0);
|
||||||
|
}
|
1
res/shaders/vertex.vert
Normal file
1
res/shaders/vertex.vert
Normal file
|
@ -0,0 +1 @@
|
||||||
|
#version 450
layout (location = 0) in vec2 position;
layout (location = 1) in vec3 color;
layout (location = 0) out vec3 fragColor;
layout (set = 0, binding = 0) uniform MVPData {
mat4 world;
mat4 view;
mat4 projection;
} uniforms;
void main() {
mat4 worldview = uniforms.view * uniforms.world;
gl_Position = uniforms.projection * worldview * vec4(position, 0.0, 1.0);
fragColor = color;
}
|
Binary file not shown.
Before Width: | Height: | Size: 49 KiB |
|
@ -1,135 +0,0 @@
|
||||||
use std::{
|
|
||||||
cell::RefCell,
|
|
||||||
rc::Rc,
|
|
||||||
sync::{Arc, RwLock},
|
|
||||||
};
|
|
||||||
|
|
||||||
use egui_winit_vulkano::Gui;
|
|
||||||
use vulkano_util::{renderer::VulkanoWindowRenderer, window::VulkanoWindows};
|
|
||||||
use winit::{event_loop::EventLoopProxy, monitor::MonitorHandle, window::WindowId};
|
|
||||||
|
|
||||||
use crate::core::{input::InputManager, render::vulkan_context::VulkanContext};
|
|
||||||
|
|
||||||
use super::user_event::UserEvent;
|
|
||||||
|
|
||||||
/// Contexte d'application unifié pour les fonctions liées à la fenêtre
|
|
||||||
pub struct WindowContext {
|
|
||||||
// Données Vulkan (immutables)
|
|
||||||
pub vulkan_context: Arc<VulkanContext>,
|
|
||||||
pub event_loop_proxy: EventLoopProxy<UserEvent>,
|
|
||||||
pub window_id: WindowId,
|
|
||||||
|
|
||||||
// Données mutables partagées avec Arc<Mutex<>>
|
|
||||||
pub vulkano_windows: Rc<RefCell<VulkanoWindows>>,
|
|
||||||
pub input_manager: Arc<RwLock<InputManager>>,
|
|
||||||
pub gui: Rc<RefCell<Gui>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WindowContext {
|
|
||||||
pub fn new(
|
|
||||||
vulkan_context: Arc<VulkanContext>,
|
|
||||||
vulkano_windows: Rc<RefCell<VulkanoWindows>>,
|
|
||||||
input_manager: Arc<RwLock<InputManager>>,
|
|
||||||
gui: Rc<RefCell<Gui>>,
|
|
||||||
event_loop_proxy: EventLoopProxy<UserEvent>,
|
|
||||||
window_id: WindowId,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
// Données Vulkan
|
|
||||||
vulkan_context,
|
|
||||||
event_loop_proxy,
|
|
||||||
window_id,
|
|
||||||
|
|
||||||
// Données mutables partagées
|
|
||||||
vulkano_windows,
|
|
||||||
input_manager,
|
|
||||||
gui,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn vulkan_context(&self) -> &VulkanContext {
|
|
||||||
&self.vulkan_context
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extrait les résolutions d'un moniteur donné
|
|
||||||
fn extract_resolutions_from_monitor(monitor: MonitorHandle) -> Vec<(u32, u32)> {
|
|
||||||
let video_modes: Vec<_> = monitor.video_modes().collect();
|
|
||||||
|
|
||||||
let resolutions: Vec<(u32, u32)> = video_modes
|
|
||||||
.into_iter()
|
|
||||||
.map(|mode| {
|
|
||||||
let size = mode.size();
|
|
||||||
(size.width, size.height)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
tracing::trace!(
|
|
||||||
"Modes vidéo trouvés pour {:?}: {:?}",
|
|
||||||
monitor.name(),
|
|
||||||
resolutions
|
|
||||||
);
|
|
||||||
resolutions
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Récupère les résolutions disponibles
|
|
||||||
pub fn get_available_resolutions(&self) -> Vec<(u32, u32)> {
|
|
||||||
self.with_renderer(|renderer| {
|
|
||||||
renderer
|
|
||||||
.window()
|
|
||||||
.current_monitor()
|
|
||||||
.map(Self::extract_resolutions_from_monitor)
|
|
||||||
.unwrap_or_default()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Récupère la taille de la fenêtre depuis le renderer
|
|
||||||
pub fn get_window_size(&self) -> [f32; 2] {
|
|
||||||
self.with_renderer(|renderer| renderer.window_size())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Récupère l'aspect ratio depuis le renderer
|
|
||||||
pub fn get_aspect_ratio(&self) -> f32 {
|
|
||||||
self.with_renderer(|renderer| renderer.aspect_ratio())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_renderer<T, F>(&self, f: F) -> T
|
|
||||||
where
|
|
||||||
F: FnOnce(&VulkanoWindowRenderer) -> T,
|
|
||||||
{
|
|
||||||
let vulkano_windows = self.vulkano_windows.borrow();
|
|
||||||
let renderer = vulkano_windows
|
|
||||||
.get_renderer(self.window_id)
|
|
||||||
.expect("Failed to get renderer");
|
|
||||||
f(renderer)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Méthode utilitaire pour accéder au renderer de manière thread-safe
|
|
||||||
pub fn with_renderer_mut<T, F>(&mut self, f: F) -> T
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut VulkanoWindowRenderer) -> T,
|
|
||||||
{
|
|
||||||
let mut vulkano_windows = self.vulkano_windows.borrow_mut();
|
|
||||||
let renderer = vulkano_windows
|
|
||||||
.get_renderer_mut(self.window_id)
|
|
||||||
.expect("Failed to get renderer");
|
|
||||||
f(renderer)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Méthode utilitaire pour accéder au gui de manière thread-safe
|
|
||||||
pub fn with_gui<T, F>(&self, f: F) -> T
|
|
||||||
where
|
|
||||||
F: FnOnce(&Gui) -> T,
|
|
||||||
{
|
|
||||||
let gui = self.gui.borrow();
|
|
||||||
f(&gui)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Méthode utilitaire pour accéder au gui de manière thread-safe
|
|
||||||
pub fn with_gui_mut<T, F>(&mut self, f: F) -> T
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut Gui) -> T,
|
|
||||||
{
|
|
||||||
let mut gui = self.gui.borrow_mut();
|
|
||||||
f(&mut gui)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,253 +0,0 @@
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::rc::Rc;
|
|
||||||
use std::sync::{Arc, RwLock};
|
|
||||||
|
|
||||||
use super::render::vulkan_context::VulkanContext;
|
|
||||||
use crate::core::input::InputManager;
|
|
||||||
use crate::core::scene::manager::SceneManager;
|
|
||||||
use crate::game::scenes::main_scene::MainScene;
|
|
||||||
use egui_winit_vulkano::{Gui, GuiConfig};
|
|
||||||
use user_event::UserEvent;
|
|
||||||
use vulkano::format::Format;
|
|
||||||
use vulkano::image::ImageUsage;
|
|
||||||
use vulkano::swapchain::PresentMode;
|
|
||||||
use vulkano_util::context::VulkanoContext;
|
|
||||||
use vulkano_util::window::{VulkanoWindows, WindowDescriptor};
|
|
||||||
use winit::application::ApplicationHandler;
|
|
||||||
use winit::event::WindowEvent;
|
|
||||||
use winit::event_loop::{ActiveEventLoop, EventLoopProxy};
|
|
||||||
use winit::window::WindowId;
|
|
||||||
|
|
||||||
use self::context::WindowContext;
|
|
||||||
|
|
||||||
pub mod context;
|
|
||||||
pub mod user_event;
|
|
||||||
|
|
||||||
pub const DEPTH_IMAGE_ID: usize = 0;
|
|
||||||
|
|
||||||
pub struct App {
|
|
||||||
vulkan_context: Arc<VulkanContext>,
|
|
||||||
vulkano_windows: Rc<RefCell<VulkanoWindows>>,
|
|
||||||
gui: HashMap<WindowId, Rc<RefCell<Gui>>>,
|
|
||||||
scene_manager: HashMap<WindowId, SceneManager>,
|
|
||||||
input_manager: Arc<RwLock<InputManager>>,
|
|
||||||
event_loop_proxy: EventLoopProxy<UserEvent>,
|
|
||||||
|
|
||||||
// Context d'application partagé par fenêtre - architecture unifiée
|
|
||||||
app_contexts: HashMap<WindowId, Rc<RefCell<WindowContext>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl App {
|
|
||||||
pub fn new(
|
|
||||||
vulkano_context: VulkanoContext,
|
|
||||||
input_manager: InputManager,
|
|
||||||
event_loop_proxy: EventLoopProxy<UserEvent>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
vulkan_context: Arc::new(VulkanContext::new(vulkano_context)),
|
|
||||||
vulkano_windows: Rc::new(RefCell::new(VulkanoWindows::default())),
|
|
||||||
gui: HashMap::new(),
|
|
||||||
input_manager: Arc::new(RwLock::new(input_manager)),
|
|
||||||
scene_manager: HashMap::new(),
|
|
||||||
event_loop_proxy,
|
|
||||||
app_contexts: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ApplicationHandler<UserEvent> for App {
|
|
||||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
|
||||||
let mut vulkano_windows = self.vulkano_windows.borrow_mut();
|
|
||||||
let window_id = vulkano_windows.create_window(
|
|
||||||
event_loop,
|
|
||||||
self.vulkan_context.vulkano_context(),
|
|
||||||
&WindowDescriptor {
|
|
||||||
title: "Rust ASH Test".to_string(),
|
|
||||||
width: 800.0,
|
|
||||||
height: 600.0,
|
|
||||||
present_mode: PresentMode::Fifo,
|
|
||||||
cursor_visible: false,
|
|
||||||
cursor_locked: true,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
|_| {},
|
|
||||||
);
|
|
||||||
|
|
||||||
let renderer = vulkano_windows.get_renderer_mut(window_id).unwrap();
|
|
||||||
renderer.add_additional_image_view(
|
|
||||||
DEPTH_IMAGE_ID,
|
|
||||||
Format::D16_UNORM,
|
|
||||||
ImageUsage::DEPTH_STENCIL_ATTACHMENT,
|
|
||||||
);
|
|
||||||
|
|
||||||
let gui = {
|
|
||||||
Gui::new(
|
|
||||||
event_loop,
|
|
||||||
renderer.surface(),
|
|
||||||
renderer.graphics_queue(),
|
|
||||||
renderer.swapchain_format(),
|
|
||||||
GuiConfig {
|
|
||||||
is_overlay: true,
|
|
||||||
allow_srgb_render_target: true,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
};
|
|
||||||
self.gui.insert(window_id, Rc::new(RefCell::new(gui)));
|
|
||||||
|
|
||||||
// Create the WindowContext with simplified arguments
|
|
||||||
let app_context = Rc::new(RefCell::new(WindowContext::new(
|
|
||||||
self.vulkan_context.clone(),
|
|
||||||
self.vulkano_windows.clone(),
|
|
||||||
self.input_manager.clone(),
|
|
||||||
self.gui.get(&window_id).unwrap().clone(),
|
|
||||||
self.event_loop_proxy.clone(),
|
|
||||||
window_id,
|
|
||||||
)));
|
|
||||||
self.app_contexts.insert(window_id, app_context.clone());
|
|
||||||
|
|
||||||
// Now use the created context to load the scene
|
|
||||||
let mut scene_manager = SceneManager::new();
|
|
||||||
scene_manager.set_new_scene(Box::new(MainScene::default()));
|
|
||||||
|
|
||||||
self.scene_manager.insert(window_id, scene_manager);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn device_event(
|
|
||||||
&mut self,
|
|
||||||
_event_loop: &ActiveEventLoop,
|
|
||||||
_device_id: winit::event::DeviceId,
|
|
||||||
event: winit::event::DeviceEvent,
|
|
||||||
) {
|
|
||||||
let mut input_manager = self.input_manager.write().unwrap();
|
|
||||||
input_manager.process_device_event(&event);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn window_event(&mut self, event_loop: &ActiveEventLoop, id: WindowId, event: WindowEvent) {
|
|
||||||
{
|
|
||||||
let gui = self.gui.get_mut(&id).unwrap();
|
|
||||||
let mut gui = gui.borrow_mut();
|
|
||||||
if !gui.update(&event) {
|
|
||||||
let mut input_manager = self.input_manager.write().unwrap();
|
|
||||||
input_manager.process_window_event(&event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match event {
|
|
||||||
WindowEvent::CloseRequested => {
|
|
||||||
tracing::debug!("The close button was pressed; stopping");
|
|
||||||
event_loop.exit();
|
|
||||||
}
|
|
||||||
WindowEvent::Resized(_) | WindowEvent::ScaleFactorChanged { .. } => {
|
|
||||||
let mut vulkano_windows = self.vulkano_windows.borrow_mut();
|
|
||||||
vulkano_windows.get_renderer_mut(id).unwrap().resize();
|
|
||||||
}
|
|
||||||
WindowEvent::RedrawRequested => {
|
|
||||||
let _frame_span = tracing::info_span!("frame").entered();
|
|
||||||
|
|
||||||
{
|
|
||||||
let _input_span = tracing::debug_span!("input_update").entered();
|
|
||||||
let mut input_manager = self
|
|
||||||
.input_manager
|
|
||||||
.write()
|
|
||||||
.expect("Failed to lock input manager");
|
|
||||||
input_manager.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Créer ou mettre à jour le contexte d'application
|
|
||||||
let window_context = self.app_contexts.get(&id).unwrap().clone();
|
|
||||||
let scene_manager = self.scene_manager.get_mut(&id).unwrap();
|
|
||||||
|
|
||||||
// Utiliser le contexte partagé pour les scènes
|
|
||||||
{
|
|
||||||
let mut context = window_context.borrow_mut();
|
|
||||||
|
|
||||||
{
|
|
||||||
let _scene_span =
|
|
||||||
tracing::info_span!("scene_loading_if_not_loaded").entered();
|
|
||||||
scene_manager
|
|
||||||
.load_scene_if_not_loaded(&mut context)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(scene) = scene_manager.current_scene_mut() {
|
|
||||||
{
|
|
||||||
let _update_span = tracing::debug_span!("scene_update").entered();
|
|
||||||
scene.update(&context).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let acquire_future = {
|
|
||||||
let _acquire_span = tracing::debug_span!("acquire_swapchain").entered();
|
|
||||||
context.with_renderer_mut(|renderer| {
|
|
||||||
renderer.acquire(None, |_| {}).unwrap()
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
let acquire_future = {
|
|
||||||
let _render_span = tracing::debug_span!("scene_render").entered();
|
|
||||||
scene.render(acquire_future, &mut context).unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
{
|
|
||||||
let _present_span = tracing::debug_span!("present_frame").entered();
|
|
||||||
context.with_renderer_mut(|renderer| {
|
|
||||||
renderer.present(acquire_future, true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
tracing::warn!("No current scene found for update!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
let _gui_span = tracing::debug_span!("request_redraw").entered();
|
|
||||||
let mut window_context = window_context.borrow_mut();
|
|
||||||
window_context.with_renderer_mut(|renderer| {
|
|
||||||
renderer.window().request_redraw();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: UserEvent) {
|
|
||||||
match event {
|
|
||||||
UserEvent::CursorGrabMode(window_id, grab) => {
|
|
||||||
let vulkano_windows = self.vulkano_windows.borrow();
|
|
||||||
let window = vulkano_windows.get_window(window_id).unwrap();
|
|
||||||
if let Err(e) = window.set_cursor_grab(grab) {
|
|
||||||
tracing::error!("Failed to set cursor grab: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UserEvent::CursorVisible(window_id, visible) => {
|
|
||||||
let vulkano_windows = self.vulkano_windows.borrow();
|
|
||||||
let window = vulkano_windows.get_window(window_id).unwrap();
|
|
||||||
window.set_cursor_visible(visible);
|
|
||||||
}
|
|
||||||
UserEvent::ChangeScene(window_id, scene) => {
|
|
||||||
if let Some(scene_manager) = self.scene_manager.get_mut(&window_id) {
|
|
||||||
scene_manager.set_new_scene(scene);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UserEvent::ChangeResolution(window_id, width, height) => {
|
|
||||||
let mut vulkano_windows = self.vulkano_windows.borrow_mut();
|
|
||||||
let window = vulkano_windows.get_window(window_id).unwrap();
|
|
||||||
let _ = window.request_inner_size(winit::dpi::LogicalSize::new(width, height));
|
|
||||||
let renderer = vulkano_windows.get_renderer_mut(window_id).unwrap();
|
|
||||||
renderer.resize();
|
|
||||||
tracing::trace!(
|
|
||||||
"Resolution changed to {}x{} for window {:?}",
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
window_id
|
|
||||||
);
|
|
||||||
}
|
|
||||||
UserEvent::Exit(window_id) => {
|
|
||||||
tracing::trace!("Exit requested for window {:?}", window_id);
|
|
||||||
event_loop.exit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
use winit::window::{CursorGrabMode, WindowId};
|
|
||||||
|
|
||||||
use crate::core::scene::AsScene;
|
|
||||||
|
|
||||||
pub enum UserEvent {
|
|
||||||
CursorGrabMode(WindowId, CursorGrabMode),
|
|
||||||
CursorVisible(WindowId, bool),
|
|
||||||
ChangeScene(WindowId, Box<dyn AsScene>),
|
|
||||||
ChangeResolution(WindowId, f32, f32),
|
|
||||||
Exit(WindowId),
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
use std::{
|
|
||||||
collections::HashMap,
|
|
||||||
hash::Hash,
|
|
||||||
ops::{Add, AddAssign, Sub},
|
|
||||||
};
|
|
||||||
|
|
||||||
use winit::event::ElementState;
|
|
||||||
|
|
||||||
pub struct CachedElementState<K: Eq + Hash> {
|
|
||||||
cache: HashMap<K, ElementState>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K: Eq + Hash> Default for CachedElementState<K> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
cache: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K: Eq + Hash> CachedElementState<K> {
|
|
||||||
pub fn set_key_state(&mut self, key: K, state: ElementState) -> Option<ElementState> {
|
|
||||||
let key_state = self.cache.get(&key);
|
|
||||||
let new_key_state = match key_state {
|
|
||||||
Some(old) => match state {
|
|
||||||
ElementState::Pressed => match old {
|
|
||||||
ElementState::Released => Some(ElementState::Pressed),
|
|
||||||
ElementState::Pressed => None,
|
|
||||||
},
|
|
||||||
ElementState::Released => match old {
|
|
||||||
ElementState::Released => None,
|
|
||||||
ElementState::Pressed => Some(ElementState::Released),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
None => match state {
|
|
||||||
ElementState::Pressed => Some(ElementState::Pressed),
|
|
||||||
ElementState::Released => Some(ElementState::Released),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
if let Some(new_key_state) = new_key_state {
|
|
||||||
self.cache.insert(key, new_key_state);
|
|
||||||
}
|
|
||||||
new_key_state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct CachedMovement<T>
|
|
||||||
where
|
|
||||||
T: Sub<Output = T> + Add<Output = T> + Default + Copy,
|
|
||||||
{
|
|
||||||
pub old_value: Option<T>,
|
|
||||||
pub value: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> CachedMovement<T>
|
|
||||||
where
|
|
||||||
T: Sub<Output = T> + Add<Output = T> + Default + Copy,
|
|
||||||
{
|
|
||||||
pub fn set_value(&mut self, value: T) {
|
|
||||||
self.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reset(&mut self) -> T {
|
|
||||||
match self.old_value.as_ref() {
|
|
||||||
Some(old_value) => {
|
|
||||||
let diff = self.value - *old_value;
|
|
||||||
self.old_value = Some(self.value);
|
|
||||||
diff
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
self.old_value = Some(self.value);
|
|
||||||
T::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> AddAssign<T> for CachedMovement<T>
|
|
||||||
where
|
|
||||||
T: Add<Output = T> + Sub<Output = T> + Default + Copy,
|
|
||||||
{
|
|
||||||
fn add_assign(&mut self, rhs: T) {
|
|
||||||
self.value = self.value + rhs;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,104 +0,0 @@
|
||||||
use std::{
|
|
||||||
collections::HashMap,
|
|
||||||
sync::{Arc, RwLock},
|
|
||||||
};
|
|
||||||
|
|
||||||
use bevy_ecs::resource::Resource;
|
|
||||||
use cache::{CachedElementState, CachedMovement};
|
|
||||||
use virtual_input::VirtualInput;
|
|
||||||
use winit::{
|
|
||||||
event::{DeviceEvent, MouseButton, MouseScrollDelta, WindowEvent},
|
|
||||||
keyboard::PhysicalKey,
|
|
||||||
};
|
|
||||||
|
|
||||||
mod cache;
|
|
||||||
mod virtual_binding;
|
|
||||||
mod virtual_input;
|
|
||||||
mod virtual_state;
|
|
||||||
pub use virtual_binding::{AxisDirection, VirtualBinding};
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct InputManager {
|
|
||||||
keys_state: CachedElementState<PhysicalKey>,
|
|
||||||
mouse_buttons_state: CachedElementState<MouseButton>,
|
|
||||||
mouse_position_delta: CachedMovement<glam::Vec2>,
|
|
||||||
mouse_wheel_delta: CachedMovement<glam::Vec2>,
|
|
||||||
virtual_input: VirtualInput,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Resource)]
|
|
||||||
pub struct InputManagerResource(pub Arc<RwLock<InputManager>>);
|
|
||||||
|
|
||||||
impl std::fmt::Debug for InputManager {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.debug_struct("InputManager")
|
|
||||||
.field("virtual_input", &self.virtual_input)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InputManager {
|
|
||||||
pub fn new(input_mapping: HashMap<String, Vec<VirtualBinding>>) -> Self {
|
|
||||||
let mut input_manager = InputManager::default();
|
|
||||||
for (value_name, bindings) in input_mapping {
|
|
||||||
input_manager.add_virtual_bindings(value_name, bindings);
|
|
||||||
}
|
|
||||||
input_manager
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn process_device_event(&mut self, event: &DeviceEvent) {
|
|
||||||
if let DeviceEvent::MouseMotion { delta, .. } = event {
|
|
||||||
self.mouse_position_delta += glam::Vec2::new(delta.0 as f32, delta.1 as f32);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn process_window_event(&mut self, event: &WindowEvent) {
|
|
||||||
match event {
|
|
||||||
WindowEvent::AxisMotion { axis, value, .. } => {
|
|
||||||
self.virtual_input.update_axis_binding(*axis, *value as f32);
|
|
||||||
}
|
|
||||||
WindowEvent::KeyboardInput { event, .. } => {
|
|
||||||
let new_key_state = self
|
|
||||||
.keys_state
|
|
||||||
.set_key_state(event.physical_key, event.state);
|
|
||||||
if let Some(new_key_state) = new_key_state {
|
|
||||||
self.virtual_input
|
|
||||||
.update_key_binding(event.physical_key, new_key_state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
WindowEvent::MouseInput { button, state, .. } => {
|
|
||||||
let new_mouse_button_state =
|
|
||||||
self.mouse_buttons_state.set_key_state(*button, *state);
|
|
||||||
if let Some(new_mouse_button_state) = new_mouse_button_state {
|
|
||||||
self.virtual_input
|
|
||||||
.update_mouse_button_binding(*button, new_mouse_button_state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
WindowEvent::MouseWheel { delta, .. } => {
|
|
||||||
self.mouse_wheel_delta += match delta {
|
|
||||||
MouseScrollDelta::PixelDelta(position) => {
|
|
||||||
glam::Vec2::new(position.x as f32, position.y as f32)
|
|
||||||
}
|
|
||||||
MouseScrollDelta::LineDelta(x, y) => glam::Vec2::new(*x, *y),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Updates deltas before running update
|
|
||||||
pub fn update(&mut self) {
|
|
||||||
self.virtual_input
|
|
||||||
.update_mouse_move_binding(&self.mouse_position_delta.reset());
|
|
||||||
self.virtual_input
|
|
||||||
.update_mouse_wheel_binding(&self.mouse_wheel_delta.reset());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_virtual_input_state(&self, value_name: &str) -> f32 {
|
|
||||||
self.virtual_input.get_state(value_name)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_virtual_bindings(&mut self, value_name: String, bindings: Vec<VirtualBinding>) {
|
|
||||||
self.virtual_input.add_bindings(value_name, bindings);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
use winit::{
|
|
||||||
event::{AxisId, MouseButton},
|
|
||||||
keyboard::PhysicalKey,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum AxisDirection {
|
|
||||||
Normal,
|
|
||||||
Invert,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&AxisDirection> for f32 {
|
|
||||||
fn from(direction: &AxisDirection) -> Self {
|
|
||||||
match direction {
|
|
||||||
AxisDirection::Normal => 1.0,
|
|
||||||
AxisDirection::Invert => -1.0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum VirtualBinding {
|
|
||||||
Keyboard(PhysicalKey, AxisDirection),
|
|
||||||
Axis(AxisId, AxisDirection, f32), // f32 deadzone
|
|
||||||
MouseX(AxisDirection),
|
|
||||||
MouseY(AxisDirection),
|
|
||||||
MouseWheelX(AxisDirection),
|
|
||||||
MouseWheelY(AxisDirection),
|
|
||||||
MouseButton(MouseButton, AxisDirection),
|
|
||||||
}
|
|
|
@ -1,150 +0,0 @@
|
||||||
use std::{
|
|
||||||
collections::HashMap,
|
|
||||||
sync::{Arc, RwLock},
|
|
||||||
};
|
|
||||||
|
|
||||||
use winit::{
|
|
||||||
event::{AxisId, ElementState, MouseButton},
|
|
||||||
keyboard::PhysicalKey,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
virtual_binding::VirtualBinding,
|
|
||||||
virtual_state::{VirtualBindingState, VirtualInputState},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct VirtualInput {
|
|
||||||
// Global states
|
|
||||||
states: HashMap<String, Arc<RwLock<VirtualInputState>>>,
|
|
||||||
|
|
||||||
// Per kind of input states to keep complexity low during state updates
|
|
||||||
states_by_key: HashMap<PhysicalKey, Vec<Arc<RwLock<VirtualInputState>>>>,
|
|
||||||
mouse_move_states: Vec<Arc<RwLock<VirtualInputState>>>,
|
|
||||||
mouse_wheel_states: Vec<Arc<RwLock<VirtualInputState>>>,
|
|
||||||
mouse_button_states: HashMap<MouseButton, Vec<Arc<RwLock<VirtualInputState>>>>,
|
|
||||||
axis_states: HashMap<AxisId, Vec<Arc<RwLock<VirtualInputState>>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Debug for VirtualInput {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let mut debug = f.debug_struct("VirtualInput");
|
|
||||||
|
|
||||||
for (name, state) in &self.states {
|
|
||||||
let value = state.read().expect("Poisoned lock for debug").value;
|
|
||||||
debug.field(name, &value);
|
|
||||||
}
|
|
||||||
|
|
||||||
debug.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VirtualInput {
|
|
||||||
pub fn get_state(&self, value_name: &str) -> f32 {
|
|
||||||
self.states
|
|
||||||
.get(value_name)
|
|
||||||
.map(|state| state.read().expect("Poisoned lock for get state").value)
|
|
||||||
.unwrap_or(0.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_bindings(&mut self, value_name: String, new_bindings: Vec<VirtualBinding>) {
|
|
||||||
let state = self
|
|
||||||
.states
|
|
||||||
.entry(value_name)
|
|
||||||
.or_insert(Arc::new(RwLock::new(VirtualInputState {
|
|
||||||
value: 0.0,
|
|
||||||
bindings: Vec::new(),
|
|
||||||
})));
|
|
||||||
|
|
||||||
for binding in &new_bindings {
|
|
||||||
match binding {
|
|
||||||
VirtualBinding::Keyboard(key, _) => {
|
|
||||||
self.states_by_key
|
|
||||||
.entry(*key)
|
|
||||||
.or_default()
|
|
||||||
.push(state.clone());
|
|
||||||
}
|
|
||||||
VirtualBinding::MouseX(_) | VirtualBinding::MouseY(_) => {
|
|
||||||
self.mouse_move_states.push(state.clone());
|
|
||||||
}
|
|
||||||
VirtualBinding::MouseButton(button, _) => {
|
|
||||||
self.mouse_button_states
|
|
||||||
.entry(*button)
|
|
||||||
.or_default()
|
|
||||||
.push(state.clone());
|
|
||||||
}
|
|
||||||
VirtualBinding::MouseWheelX(_) | VirtualBinding::MouseWheelY(_) => {
|
|
||||||
self.mouse_wheel_states.push(state.clone());
|
|
||||||
}
|
|
||||||
VirtualBinding::Axis(axis, _, _) => {
|
|
||||||
self.axis_states
|
|
||||||
.entry(*axis)
|
|
||||||
.or_default()
|
|
||||||
.push(state.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
state
|
|
||||||
.write()
|
|
||||||
.expect("Poisoned lock for add bindings")
|
|
||||||
.bindings
|
|
||||||
.extend(new_bindings.iter().map(|b| VirtualBindingState {
|
|
||||||
value: 0.0,
|
|
||||||
binding: b.clone(),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn update_key_binding(&mut self, key: PhysicalKey, key_state: ElementState) {
|
|
||||||
let states = self.states_by_key.get_mut(&key);
|
|
||||||
|
|
||||||
if let Some(states) = states {
|
|
||||||
for state in states {
|
|
||||||
let mut state = state.write().expect("Poisoned lock for key update");
|
|
||||||
state.update_from_key(key, key_state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn update_mouse_move_binding(&mut self, delta: &glam::Vec2) {
|
|
||||||
for state in &mut self.mouse_move_states {
|
|
||||||
let mut state = state.write().expect("Poisoned lock for mouse move update");
|
|
||||||
state.update_from_mouse(delta);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn update_mouse_wheel_binding(&mut self, delta: &glam::Vec2) {
|
|
||||||
for state in &mut self.mouse_wheel_states {
|
|
||||||
let mut state = state.write().expect("Poisoned lock for mouse wheel update");
|
|
||||||
state.update_from_mouse_wheel(delta);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn update_mouse_button_binding(
|
|
||||||
&mut self,
|
|
||||||
button: MouseButton,
|
|
||||||
button_state: ElementState,
|
|
||||||
) {
|
|
||||||
let states = self.mouse_button_states.get_mut(&button);
|
|
||||||
|
|
||||||
if let Some(states) = states {
|
|
||||||
for state in states {
|
|
||||||
let mut state = state
|
|
||||||
.write()
|
|
||||||
.expect("Poisoned lock for mouse button update");
|
|
||||||
state.update_from_mouse_button(button, button_state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn update_axis_binding(&mut self, axis: AxisId, axis_state: f32) {
|
|
||||||
let states = self.axis_states.get_mut(&axis);
|
|
||||||
|
|
||||||
if let Some(states) = states {
|
|
||||||
for state in states {
|
|
||||||
let mut state = state.write().expect("Poisoned lock for axis update");
|
|
||||||
state.update_from_axis(axis, axis_state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,105 +0,0 @@
|
||||||
use winit::{
|
|
||||||
event::{AxisId, ElementState, MouseButton},
|
|
||||||
keyboard::PhysicalKey,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::virtual_binding::VirtualBinding;
|
|
||||||
|
|
||||||
pub struct VirtualBindingState {
|
|
||||||
pub value: f32,
|
|
||||||
pub binding: VirtualBinding,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct VirtualInputState {
|
|
||||||
pub value: f32,
|
|
||||||
pub bindings: Vec<VirtualBindingState>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VirtualInputState {
|
|
||||||
pub fn update_from_key(&mut self, key: PhysicalKey, key_state: ElementState) {
|
|
||||||
let mut new_value = 0.0;
|
|
||||||
for binding in &mut self.bindings {
|
|
||||||
if let VirtualBinding::Keyboard(binding_key, direction) = &binding.binding {
|
|
||||||
if binding_key == &key {
|
|
||||||
if key_state == ElementState::Pressed {
|
|
||||||
binding.value += f32::from(direction);
|
|
||||||
} else {
|
|
||||||
binding.value = 0.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
new_value += binding.value;
|
|
||||||
}
|
|
||||||
self.value = new_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_from_mouse(&mut self, delta: &glam::Vec2) {
|
|
||||||
let mut new_value = 0.0;
|
|
||||||
for binding in &mut self.bindings {
|
|
||||||
match &binding.binding {
|
|
||||||
VirtualBinding::MouseX(direction) => {
|
|
||||||
binding.value = f32::from(direction) * delta.x;
|
|
||||||
}
|
|
||||||
VirtualBinding::MouseY(direction) => {
|
|
||||||
binding.value = f32::from(direction) * delta.y;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
new_value += binding.value;
|
|
||||||
}
|
|
||||||
self.value = new_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_from_mouse_wheel(&mut self, delta: &glam::Vec2) {
|
|
||||||
let mut new_value = 0.0;
|
|
||||||
for binding in &mut self.bindings {
|
|
||||||
match &binding.binding {
|
|
||||||
VirtualBinding::MouseWheelX(direction) => {
|
|
||||||
binding.value = f32::from(direction) * delta.x;
|
|
||||||
}
|
|
||||||
VirtualBinding::MouseWheelY(direction) => {
|
|
||||||
binding.value = f32::from(direction) * delta.y;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
new_value += binding.value;
|
|
||||||
}
|
|
||||||
self.value = new_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_from_mouse_button(&mut self, button: MouseButton, button_state: ElementState) {
|
|
||||||
let mut new_value = 0.0;
|
|
||||||
for binding in &mut self.bindings {
|
|
||||||
if let VirtualBinding::MouseButton(binding_button, direction) = &binding.binding {
|
|
||||||
if binding_button == &button {
|
|
||||||
if button_state == ElementState::Pressed {
|
|
||||||
binding.value = f32::from(direction);
|
|
||||||
} else {
|
|
||||||
binding.value = 0.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
new_value += binding.value;
|
|
||||||
}
|
|
||||||
self.value = new_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_from_axis(&mut self, axis: AxisId, axis_state: f32) {
|
|
||||||
let mut new_value = 0.0;
|
|
||||||
for binding in &mut self.bindings {
|
|
||||||
if let VirtualBinding::Axis(binding_axis, direction, deadzone) = &binding.binding {
|
|
||||||
if binding_axis == &axis {
|
|
||||||
binding.value =
|
|
||||||
f32::from(direction) * process_axis_deadzone(axis_state, *deadzone);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
new_value += binding.value;
|
|
||||||
}
|
|
||||||
self.value = new_value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn process_axis_deadzone(value: f32, deadzone: f32) -> f32 {
|
|
||||||
if value.abs() < deadzone { 0.0 } else { value }
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
pub mod app;
|
|
||||||
pub mod input;
|
|
||||||
pub mod render;
|
|
||||||
pub mod scene;
|
|
|
@ -1,4 +0,0 @@
|
||||||
pub mod primitives;
|
|
||||||
pub mod render_pass_manager;
|
|
||||||
pub mod resources;
|
|
||||||
pub mod vulkan_context;
|
|
|
@ -1,84 +0,0 @@
|
||||||
use std::{error::Error, sync::Arc};
|
|
||||||
|
|
||||||
use vulkano::{
|
|
||||||
Validated,
|
|
||||||
buffer::{AllocateBufferError, Buffer, BufferContents, BufferCreateInfo, Subbuffer},
|
|
||||||
memory::allocator::{AllocationCreateInfo, StandardMemoryAllocator},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub trait AsBindableBuffer {
|
|
||||||
type BufferData: BufferContents + Clone;
|
|
||||||
|
|
||||||
fn buffer_create_info() -> BufferCreateInfo;
|
|
||||||
|
|
||||||
fn allocation_create_info() -> AllocationCreateInfo;
|
|
||||||
|
|
||||||
fn to_buffer_data(&self) -> Self::BufferData;
|
|
||||||
|
|
||||||
fn create_buffer(
|
|
||||||
&self,
|
|
||||||
memory_allocator: &Arc<StandardMemoryAllocator>,
|
|
||||||
) -> Result<Subbuffer<[Self::BufferData]>, Validated<AllocateBufferError>> {
|
|
||||||
Buffer::from_iter(
|
|
||||||
memory_allocator.clone(),
|
|
||||||
Self::buffer_create_info(),
|
|
||||||
Self::allocation_create_info(),
|
|
||||||
[self.to_buffer_data()],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_buffer(&self, buffer: &Subbuffer<[Self::BufferData]>) -> Result<(), Box<dyn Error>> {
|
|
||||||
let mut write_guard = buffer.write()?;
|
|
||||||
write_guard[0] = self.to_buffer_data();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait AsUniformBuffer: AsBindableBuffer {
|
|
||||||
fn create_uniform_buffer(
|
|
||||||
&self,
|
|
||||||
memory_allocator: &Arc<StandardMemoryAllocator>,
|
|
||||||
) -> Result<Subbuffer<[Self::BufferData]>, Validated<AllocateBufferError>> {
|
|
||||||
self.create_buffer(memory_allocator)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait AsVertexBuffer: AsBindableBuffer
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
fn create_vertex_buffer(
|
|
||||||
memory_allocator: &Arc<StandardMemoryAllocator>,
|
|
||||||
vertices: &[Self],
|
|
||||||
) -> Result<Subbuffer<[Self::BufferData]>, Validated<AllocateBufferError>> {
|
|
||||||
let buffer_data: Vec<Self::BufferData> =
|
|
||||||
vertices.iter().map(|v| v.to_buffer_data()).collect();
|
|
||||||
|
|
||||||
Buffer::from_iter(
|
|
||||||
memory_allocator.clone(),
|
|
||||||
Self::buffer_create_info(),
|
|
||||||
Self::allocation_create_info(),
|
|
||||||
buffer_data,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait AsIndexBuffer: AsBindableBuffer
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
fn create_index_buffer(
|
|
||||||
memory_allocator: &Arc<StandardMemoryAllocator>,
|
|
||||||
indices: &[Self],
|
|
||||||
) -> Result<Subbuffer<[Self::BufferData]>, Validated<AllocateBufferError>> {
|
|
||||||
let buffer_data: Vec<Self::BufferData> =
|
|
||||||
indices.iter().map(|i| i.to_buffer_data()).collect();
|
|
||||||
|
|
||||||
Buffer::from_iter(
|
|
||||||
memory_allocator.clone(),
|
|
||||||
Self::buffer_create_info(),
|
|
||||||
Self::allocation_create_info(),
|
|
||||||
buffer_data,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,81 +0,0 @@
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use bevy_ecs::component::Component;
|
|
||||||
use glam::{Mat4, Vec3, Vec4};
|
|
||||||
use vulkano::{
|
|
||||||
Validated,
|
|
||||||
buffer::{AllocateBufferError, Subbuffer},
|
|
||||||
memory::allocator::StandardMemoryAllocator,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{AsUniformBuffer, mvp::Mvp};
|
|
||||||
|
|
||||||
// See docs/OPENGL_VULKAN_DIFF.md
|
|
||||||
const OPENGL_TO_VULKAN_Y_AXIS_FLIP: Mat4 = Mat4 {
|
|
||||||
x_axis: Vec4::new(1.0, 0.0, 0.0, 0.0),
|
|
||||||
y_axis: Vec4::new(0.0, -1.0, 0.0, 0.0),
|
|
||||||
z_axis: Vec4::new(0.0, 0.0, 1.0, 0.0),
|
|
||||||
w_axis: Vec4::new(0.0, 0.0, 0.0, 1.0),
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Component)]
|
|
||||||
pub struct Camera3D {
|
|
||||||
projection: Mat4,
|
|
||||||
aspect_ratio: f32,
|
|
||||||
fov: f32,
|
|
||||||
near: f32,
|
|
||||||
far: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Component)]
|
|
||||||
pub struct Camera3DTransform {
|
|
||||||
pub position: Vec3,
|
|
||||||
pub rotation: Vec3,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Camera3D {
|
|
||||||
pub fn new(aspect_ratio: f32, fov: f32, near: f32, far: f32) -> Self {
|
|
||||||
Self {
|
|
||||||
projection: Mat4::perspective_rh(fov, aspect_ratio, near, far),
|
|
||||||
aspect_ratio,
|
|
||||||
fov,
|
|
||||||
near,
|
|
||||||
far,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_projection(&mut self, window_aspect_ratio: f32) {
|
|
||||||
if self.aspect_ratio != window_aspect_ratio {
|
|
||||||
self.aspect_ratio = window_aspect_ratio;
|
|
||||||
self.projection =
|
|
||||||
Mat4::perspective_rh(self.fov, self.aspect_ratio, self.near, self.far);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_projection(&mut self, projection: Mat4) {
|
|
||||||
self.projection = projection;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_buffer(
|
|
||||||
&self,
|
|
||||||
transform: &Camera3DTransform,
|
|
||||||
memory_allocator: &Arc<StandardMemoryAllocator>,
|
|
||||||
) -> Result<Subbuffer<[Mvp]>, Validated<AllocateBufferError>> {
|
|
||||||
let (sin_pitch, cos_pitch) = transform.rotation.x.sin_cos();
|
|
||||||
let (sin_yaw, cos_yaw) = transform.rotation.y.sin_cos();
|
|
||||||
|
|
||||||
let view_matrix = Mat4::look_to_rh(
|
|
||||||
transform.position,
|
|
||||||
Vec3::new(cos_pitch * cos_yaw, sin_pitch, cos_pitch * sin_yaw).normalize(),
|
|
||||||
Vec3::Y,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mvp = Mvp {
|
|
||||||
model: OPENGL_TO_VULKAN_Y_AXIS_FLIP.to_cols_array_2d(),
|
|
||||||
view: view_matrix.to_cols_array_2d(),
|
|
||||||
projection: self.projection.to_cols_array_2d(),
|
|
||||||
};
|
|
||||||
|
|
||||||
mvp.create_uniform_buffer(memory_allocator)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,113 +0,0 @@
|
||||||
use std::{error::Error, sync::Arc};
|
|
||||||
|
|
||||||
use vulkano::{
|
|
||||||
buffer::{BufferContents, IndexBuffer, Subbuffer},
|
|
||||||
command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer},
|
|
||||||
descriptor_set::allocator::StandardDescriptorSetAllocator,
|
|
||||||
pipeline::GraphicsPipeline,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::AsDescriptorSet;
|
|
||||||
|
|
||||||
pub trait AsRecordable {
|
|
||||||
fn record_bind_commands(
|
|
||||||
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
|
|
||||||
descriptor_set_allocator: &Arc<StandardDescriptorSetAllocator>,
|
|
||||||
pipeline: &Arc<GraphicsPipeline>,
|
|
||||||
mesh: &impl AsRenderableMesh,
|
|
||||||
instances: &impl AsRenderableMeshInstance,
|
|
||||||
descriptor_sets: Vec<Arc<dyn AsDescriptorSet>>,
|
|
||||||
) -> Result<(), Box<dyn Error>>;
|
|
||||||
|
|
||||||
fn record_draw_commands(
|
|
||||||
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
|
|
||||||
mesh: &impl AsRenderableMesh,
|
|
||||||
instances: &impl AsRenderableMeshInstance,
|
|
||||||
) -> Result<(), Box<dyn Error>> {
|
|
||||||
match mesh.index_buffer() {
|
|
||||||
Some(index_buffer) => {
|
|
||||||
builder.bind_index_buffer(index_buffer.clone())?;
|
|
||||||
unsafe {
|
|
||||||
builder.draw_indexed(
|
|
||||||
mesh.index_count(),
|
|
||||||
instances.instance_count(),
|
|
||||||
mesh.first_index(),
|
|
||||||
mesh.vertex_offset(),
|
|
||||||
instances.first_instance(),
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => unsafe {
|
|
||||||
builder.draw(
|
|
||||||
mesh.vertex_count(),
|
|
||||||
instances.instance_count(),
|
|
||||||
mesh.first_vertex(),
|
|
||||||
instances.first_instance(),
|
|
||||||
)?;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn record_commands(
|
|
||||||
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
|
|
||||||
descriptor_set_allocator: &Arc<StandardDescriptorSetAllocator>,
|
|
||||||
pipeline: &Arc<GraphicsPipeline>,
|
|
||||||
mesh: &impl AsRenderableMesh,
|
|
||||||
instances: &impl AsRenderableMeshInstance,
|
|
||||||
descriptor_sets: Vec<Arc<dyn AsDescriptorSet>>,
|
|
||||||
) -> Result<(), Box<dyn Error>> {
|
|
||||||
Self::record_bind_commands(
|
|
||||||
builder,
|
|
||||||
descriptor_set_allocator,
|
|
||||||
pipeline,
|
|
||||||
mesh,
|
|
||||||
instances,
|
|
||||||
descriptor_sets,
|
|
||||||
)?;
|
|
||||||
Self::record_draw_commands(builder, mesh, instances)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait AsRenderableMesh {
|
|
||||||
type VertexBufferData: BufferContents + Clone;
|
|
||||||
type IndexBuffer: Into<IndexBuffer> + Clone;
|
|
||||||
|
|
||||||
fn vertex_buffer(&self) -> &Subbuffer<[Self::VertexBufferData]>;
|
|
||||||
|
|
||||||
fn vertex_count(&self) -> u32;
|
|
||||||
|
|
||||||
fn first_vertex(&self) -> u32 {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
fn vertex_offset(&self) -> i32 {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
fn index_buffer(&self) -> Option<&Self::IndexBuffer> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn index_count(&self) -> u32 {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
fn first_index(&self) -> u32 {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait AsRenderableMeshInstance {
|
|
||||||
type InstanceBufferData: BufferContents + Clone;
|
|
||||||
|
|
||||||
fn instance_buffer(&self) -> &Subbuffer<[Self::InstanceBufferData]>;
|
|
||||||
|
|
||||||
fn instance_count(&self) -> u32;
|
|
||||||
|
|
||||||
fn first_instance(&self) -> u32 {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
use std::{collections::BTreeMap, sync::Arc};
|
|
||||||
|
|
||||||
use vulkano::{
|
|
||||||
Validated, VulkanError,
|
|
||||||
descriptor_set::{
|
|
||||||
DescriptorSet,
|
|
||||||
allocator::StandardDescriptorSetAllocator,
|
|
||||||
layout::{DescriptorSetLayout, DescriptorSetLayoutBinding},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub trait AsDescriptorSetLayoutBindings {
|
|
||||||
fn as_descriptor_set_layout_bindings() -> BTreeMap<u32, DescriptorSetLayoutBinding>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait AsDescriptorSet {
|
|
||||||
fn as_descriptor_set(
|
|
||||||
&self,
|
|
||||||
descriptor_set_allocator: &Arc<StandardDescriptorSetAllocator>,
|
|
||||||
layout: &Arc<DescriptorSetLayout>,
|
|
||||||
) -> Result<Arc<DescriptorSet>, Validated<VulkanError>>;
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
mod buffer;
|
|
||||||
mod command;
|
|
||||||
mod descriptor_set;
|
|
||||||
|
|
||||||
pub mod camera;
|
|
||||||
pub mod mvp;
|
|
||||||
pub mod transform;
|
|
||||||
pub mod velocity;
|
|
||||||
pub mod vertex;
|
|
||||||
pub use buffer::{AsBindableBuffer, AsIndexBuffer, AsUniformBuffer, AsVertexBuffer};
|
|
||||||
pub use command::{AsRecordable, AsRenderableMesh, AsRenderableMeshInstance};
|
|
||||||
pub use descriptor_set::{AsDescriptorSet, AsDescriptorSetLayoutBindings};
|
|
|
@ -1,87 +0,0 @@
|
||||||
use std::collections::BTreeMap;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use vulkano::buffer::{
|
|
||||||
AllocateBufferError, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer,
|
|
||||||
};
|
|
||||||
use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator;
|
|
||||||
use vulkano::descriptor_set::layout::{
|
|
||||||
DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorType,
|
|
||||||
};
|
|
||||||
use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet};
|
|
||||||
use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator};
|
|
||||||
use vulkano::shader::ShaderStages;
|
|
||||||
use vulkano::{Validated, VulkanError};
|
|
||||||
|
|
||||||
use crate::core::render::primitives::{AsBindableBuffer, AsUniformBuffer};
|
|
||||||
|
|
||||||
use super::{AsDescriptorSet, AsDescriptorSetLayoutBindings};
|
|
||||||
|
|
||||||
#[derive(BufferContents, Clone, Copy)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Mvp {
|
|
||||||
pub model: [[f32; 4]; 4],
|
|
||||||
pub view: [[f32; 4]; 4],
|
|
||||||
pub projection: [[f32; 4]; 4],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mvp {
|
|
||||||
pub fn into_buffer(
|
|
||||||
self,
|
|
||||||
memory_allocator: &Arc<StandardMemoryAllocator>,
|
|
||||||
) -> Result<Subbuffer<[Mvp]>, Validated<AllocateBufferError>> {
|
|
||||||
self.create_uniform_buffer(memory_allocator)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsBindableBuffer for Mvp {
|
|
||||||
type BufferData = Mvp;
|
|
||||||
|
|
||||||
fn buffer_create_info() -> BufferCreateInfo {
|
|
||||||
BufferCreateInfo {
|
|
||||||
usage: BufferUsage::UNIFORM_BUFFER,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn allocation_create_info() -> AllocationCreateInfo {
|
|
||||||
AllocationCreateInfo {
|
|
||||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
|
||||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_buffer_data(&self) -> Self::BufferData {
|
|
||||||
*self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsUniformBuffer for Mvp {}
|
|
||||||
|
|
||||||
impl AsDescriptorSetLayoutBindings for Mvp {
|
|
||||||
fn as_descriptor_set_layout_bindings() -> BTreeMap<u32, DescriptorSetLayoutBinding> {
|
|
||||||
BTreeMap::<u32, DescriptorSetLayoutBinding>::from_iter([(
|
|
||||||
0,
|
|
||||||
DescriptorSetLayoutBinding {
|
|
||||||
stages: ShaderStages::VERTEX,
|
|
||||||
..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::UniformBuffer)
|
|
||||||
},
|
|
||||||
)])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsDescriptorSet for Subbuffer<[Mvp]> {
|
|
||||||
fn as_descriptor_set(
|
|
||||||
&self,
|
|
||||||
descriptor_set_allocator: &Arc<StandardDescriptorSetAllocator>,
|
|
||||||
layout: &Arc<DescriptorSetLayout>,
|
|
||||||
) -> Result<Arc<DescriptorSet>, Validated<VulkanError>> {
|
|
||||||
DescriptorSet::new(
|
|
||||||
descriptor_set_allocator.clone(),
|
|
||||||
layout.clone(),
|
|
||||||
[WriteDescriptorSet::buffer(0, self.clone())],
|
|
||||||
[],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,116 +0,0 @@
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use bevy_ecs::prelude::*;
|
|
||||||
use glam::{Mat4, Quat, Vec3};
|
|
||||||
use vulkano::{
|
|
||||||
Validated,
|
|
||||||
buffer::{AllocateBufferError, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer},
|
|
||||||
memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator},
|
|
||||||
pipeline::graphics::vertex_input::Vertex,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::core::render::primitives::{AsBindableBuffer, AsVertexBuffer};
|
|
||||||
|
|
||||||
use super::command::AsRenderableMeshInstance;
|
|
||||||
|
|
||||||
#[derive(Component, Debug, Clone)]
|
|
||||||
pub struct Transform {
|
|
||||||
pub position: Vec3,
|
|
||||||
pub rotation: Quat,
|
|
||||||
pub scale: Vec3,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(BufferContents, Vertex, Clone, Copy, Debug)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct TransformRaw {
|
|
||||||
#[format(R32G32B32A32_SFLOAT)]
|
|
||||||
pub model: [[f32; 4]; 4],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Transform {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
position: Vec3::ZERO,
|
|
||||||
rotation: Quat::IDENTITY,
|
|
||||||
scale: Vec3::ONE,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Transform {
|
|
||||||
pub fn new(position: Vec3, rotation: Quat, scale: Vec3) -> Self {
|
|
||||||
Self {
|
|
||||||
position,
|
|
||||||
rotation,
|
|
||||||
scale,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the transformation matrix (immutable - recalculates each time)
|
|
||||||
pub fn matrix(&self) -> Mat4 {
|
|
||||||
Mat4::from_translation(self.position)
|
|
||||||
* Mat4::from_quat(self.rotation)
|
|
||||||
* Mat4::from_scale(self.scale)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert to GPU-ready format (immutable - recalculates each time)
|
|
||||||
pub fn to_raw(&self) -> TransformRaw {
|
|
||||||
TransformRaw {
|
|
||||||
model: self.matrix().to_cols_array_2d(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a buffer from transforms (immutable - recalculates each time)
|
|
||||||
pub fn create_buffer(
|
|
||||||
memory_allocator: &Arc<StandardMemoryAllocator>,
|
|
||||||
transforms: &[Transform],
|
|
||||||
) -> Result<Subbuffer<[TransformRaw]>, Validated<AllocateBufferError>> {
|
|
||||||
TransformRaw::create_vertex_buffer(
|
|
||||||
memory_allocator,
|
|
||||||
&transforms.iter().map(|t| t.to_raw()).collect::<Vec<_>>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&Transform> for TransformRaw {
|
|
||||||
fn from(transform: &Transform) -> Self {
|
|
||||||
transform.to_raw()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsBindableBuffer for TransformRaw {
|
|
||||||
type BufferData = TransformRaw;
|
|
||||||
|
|
||||||
fn buffer_create_info() -> BufferCreateInfo {
|
|
||||||
BufferCreateInfo {
|
|
||||||
usage: BufferUsage::VERTEX_BUFFER,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn allocation_create_info() -> AllocationCreateInfo {
|
|
||||||
AllocationCreateInfo {
|
|
||||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
|
||||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_buffer_data(&self) -> Self::BufferData {
|
|
||||||
*self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsVertexBuffer for TransformRaw {}
|
|
||||||
|
|
||||||
impl AsRenderableMeshInstance for Subbuffer<[TransformRaw]> {
|
|
||||||
type InstanceBufferData = TransformRaw;
|
|
||||||
|
|
||||||
fn instance_buffer(&self) -> &Subbuffer<[Self::InstanceBufferData]> {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn instance_count(&self) -> u32 {
|
|
||||||
self.len() as u32
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
use bevy_ecs::prelude::*;
|
|
||||||
use glam::Vec3;
|
|
||||||
|
|
||||||
#[derive(Component, Debug, Clone, Default)]
|
|
||||||
pub struct Velocity {
|
|
||||||
pub linear: Vec3,
|
|
||||||
pub angular: Vec3,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Velocity {
|
|
||||||
pub fn new(linear: Vec3, angular: Vec3) -> Self {
|
|
||||||
Self { linear, angular }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,101 +0,0 @@
|
||||||
use vulkano::buffer::BufferContents;
|
|
||||||
use vulkano::buffer::{BufferCreateInfo, BufferUsage};
|
|
||||||
use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter};
|
|
||||||
use vulkano::pipeline::graphics::vertex_input::Vertex;
|
|
||||||
|
|
||||||
use crate::core::render::primitives::{AsBindableBuffer, AsIndexBuffer, AsVertexBuffer};
|
|
||||||
|
|
||||||
#[derive(BufferContents, Vertex, Clone, Copy)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Vertex2D {
|
|
||||||
#[format(R32G32_SFLOAT)]
|
|
||||||
pub position: [f32; 2],
|
|
||||||
|
|
||||||
#[format(R32G32_SFLOAT)]
|
|
||||||
pub uv: [f32; 2],
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(BufferContents, Vertex, Clone, Copy)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Vertex3D {
|
|
||||||
#[format(R32G32B32_SFLOAT)]
|
|
||||||
pub position: [f32; 3],
|
|
||||||
|
|
||||||
#[format(R32G32_SFLOAT)]
|
|
||||||
pub uv: [f32; 2],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsBindableBuffer for Vertex2D {
|
|
||||||
type BufferData = Vertex2D;
|
|
||||||
|
|
||||||
fn buffer_create_info() -> BufferCreateInfo {
|
|
||||||
BufferCreateInfo {
|
|
||||||
usage: BufferUsage::VERTEX_BUFFER,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn allocation_create_info() -> AllocationCreateInfo {
|
|
||||||
AllocationCreateInfo {
|
|
||||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
|
||||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_buffer_data(&self) -> Self::BufferData {
|
|
||||||
*self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsVertexBuffer for Vertex2D {}
|
|
||||||
|
|
||||||
impl AsBindableBuffer for Vertex3D {
|
|
||||||
type BufferData = Vertex3D;
|
|
||||||
|
|
||||||
fn buffer_create_info() -> BufferCreateInfo {
|
|
||||||
BufferCreateInfo {
|
|
||||||
usage: BufferUsage::VERTEX_BUFFER,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn allocation_create_info() -> AllocationCreateInfo {
|
|
||||||
AllocationCreateInfo {
|
|
||||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
|
||||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_buffer_data(&self) -> Self::BufferData {
|
|
||||||
*self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsVertexBuffer for Vertex3D {}
|
|
||||||
|
|
||||||
impl AsBindableBuffer for u32 {
|
|
||||||
type BufferData = u32;
|
|
||||||
|
|
||||||
fn buffer_create_info() -> BufferCreateInfo {
|
|
||||||
BufferCreateInfo {
|
|
||||||
usage: BufferUsage::INDEX_BUFFER,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn allocation_create_info() -> AllocationCreateInfo {
|
|
||||||
AllocationCreateInfo {
|
|
||||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
|
||||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_buffer_data(&self) -> Self::BufferData {
|
|
||||||
*self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsIndexBuffer for u32 {}
|
|
|
@ -1,113 +0,0 @@
|
||||||
use std::sync::Arc;
|
|
||||||
use vulkano::{
|
|
||||||
command_buffer::{AutoCommandBufferBuilder, RenderingAttachmentInfo, RenderingInfo},
|
|
||||||
image::view::ImageView,
|
|
||||||
pipeline::graphics::viewport::Viewport,
|
|
||||||
render_pass::{AttachmentLoadOp, AttachmentStoreOp},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Types de render passes disponibles
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum RenderPassType {
|
|
||||||
Standard,
|
|
||||||
ShadowMap,
|
|
||||||
PostProcess,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Configuration pour un render pass
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct RenderPassConfig {
|
|
||||||
pub pass_type: RenderPassType,
|
|
||||||
pub clear_color: Option<[f32; 4]>,
|
|
||||||
pub clear_depth: Option<f32>,
|
|
||||||
pub load_op: AttachmentLoadOp,
|
|
||||||
pub store_op: AttachmentStoreOp,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for RenderPassConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
pass_type: RenderPassType::Standard,
|
|
||||||
clear_color: Some([0.0, 0.0, 0.0, 1.0]),
|
|
||||||
clear_depth: Some(1.0),
|
|
||||||
load_op: AttachmentLoadOp::Clear,
|
|
||||||
store_op: AttachmentStoreOp::Store,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gestionnaire de render passes réutilisable
|
|
||||||
pub struct RenderPassManager;
|
|
||||||
|
|
||||||
impl RenderPassManager {
|
|
||||||
/// Commence un render pass standard avec les paramètres donnés
|
|
||||||
pub fn begin_standard_rendering(
|
|
||||||
builder: &mut AutoCommandBufferBuilder<vulkano::command_buffer::PrimaryAutoCommandBuffer>,
|
|
||||||
config: &RenderPassConfig,
|
|
||||||
color_attachment: Arc<ImageView>,
|
|
||||||
depth_attachment: Option<Arc<ImageView>>,
|
|
||||||
window_size: [f32; 2],
|
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let viewport = Viewport {
|
|
||||||
offset: [0.0, 0.0],
|
|
||||||
extent: window_size,
|
|
||||||
depth_range: 0.0..=1.0,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut rendering_info = RenderingInfo {
|
|
||||||
color_attachments: vec![Some(RenderingAttachmentInfo {
|
|
||||||
load_op: config.load_op,
|
|
||||||
store_op: config.store_op,
|
|
||||||
clear_value: config.clear_color.map(|c| c.into()),
|
|
||||||
..RenderingAttachmentInfo::image_view(color_attachment)
|
|
||||||
})],
|
|
||||||
depth_attachment: None,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(depth_view) = depth_attachment {
|
|
||||||
rendering_info.depth_attachment = Some(RenderingAttachmentInfo {
|
|
||||||
load_op: AttachmentLoadOp::Clear,
|
|
||||||
store_op: AttachmentStoreOp::DontCare,
|
|
||||||
clear_value: config.clear_depth.map(|d| [d].into()),
|
|
||||||
..RenderingAttachmentInfo::image_view(depth_view)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
builder
|
|
||||||
.begin_rendering(rendering_info)?
|
|
||||||
.set_viewport(0, [viewport].into_iter().collect())?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Termine le render pass actuel
|
|
||||||
pub fn end_rendering(
|
|
||||||
builder: &mut AutoCommandBufferBuilder<vulkano::command_buffer::PrimaryAutoCommandBuffer>,
|
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
builder.end_rendering()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Crée une configuration pour un render pass shadow map
|
|
||||||
pub fn shadow_map_config() -> RenderPassConfig {
|
|
||||||
RenderPassConfig {
|
|
||||||
pass_type: RenderPassType::ShadowMap,
|
|
||||||
clear_color: None,
|
|
||||||
clear_depth: Some(1.0),
|
|
||||||
load_op: AttachmentLoadOp::Clear,
|
|
||||||
store_op: AttachmentStoreOp::Store,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Crée une configuration pour un render pass de post-processing
|
|
||||||
pub fn post_process_config() -> RenderPassConfig {
|
|
||||||
RenderPassConfig {
|
|
||||||
pass_type: RenderPassType::PostProcess,
|
|
||||||
clear_color: Some([0.0, 0.0, 0.0, 1.0]),
|
|
||||||
clear_depth: None,
|
|
||||||
load_op: AttachmentLoadOp::Load,
|
|
||||||
store_op: AttachmentStoreOp::Store,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
mod square;
|
|
||||||
pub use square::SquareMesh;
|
|
||||||
|
|
||||||
mod obj;
|
|
||||||
pub use obj::ObjMesh;
|
|
|
@ -1,79 +0,0 @@
|
||||||
use std::{error::Error, path::PathBuf, sync::Arc};
|
|
||||||
|
|
||||||
use vulkano::{buffer::Subbuffer, memory::allocator::StandardMemoryAllocator};
|
|
||||||
|
|
||||||
use crate::core::render::primitives::{
|
|
||||||
AsIndexBuffer, AsRenderableMesh, AsVertexBuffer, vertex::Vertex3D,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct ObjMesh {
|
|
||||||
vertex_buffer: Subbuffer<[Vertex3D]>,
|
|
||||||
index_buffer: Subbuffer<[u32]>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ObjMesh {
|
|
||||||
pub fn new(
|
|
||||||
memory_allocator: &Arc<StandardMemoryAllocator>,
|
|
||||||
file_path: impl Into<PathBuf>,
|
|
||||||
) -> Result<Vec<Self>, Box<dyn Error>> {
|
|
||||||
let (models, _) = tobj::load_obj(
|
|
||||||
&file_path.into(),
|
|
||||||
&tobj::LoadOptions {
|
|
||||||
single_index: true,
|
|
||||||
triangulate: true,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let meshes = models
|
|
||||||
.into_iter()
|
|
||||||
.map(|model| {
|
|
||||||
let vertices = (0..model.mesh.positions.len() / 3)
|
|
||||||
.map(|i| Vertex3D {
|
|
||||||
position: [
|
|
||||||
model.mesh.positions[i * 3],
|
|
||||||
model.mesh.positions[i * 3 + 1],
|
|
||||||
model.mesh.positions[i * 3 + 2],
|
|
||||||
],
|
|
||||||
uv: [model.mesh.texcoords[i * 2], model.mesh.texcoords[i * 2 + 1]],
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let indices = model.mesh.indices;
|
|
||||||
|
|
||||||
let vertex_buffer = Vertex3D::create_vertex_buffer(memory_allocator, &vertices)
|
|
||||||
.expect("Failed to create vertex buffer");
|
|
||||||
let index_buffer = u32::create_index_buffer(memory_allocator, &indices)
|
|
||||||
.expect("Failed to create index buffer");
|
|
||||||
|
|
||||||
Self {
|
|
||||||
vertex_buffer,
|
|
||||||
index_buffer,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
Ok(meshes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRenderableMesh for ObjMesh {
|
|
||||||
type VertexBufferData = Vertex3D;
|
|
||||||
type IndexBuffer = Subbuffer<[u32]>;
|
|
||||||
|
|
||||||
fn vertex_buffer(&self) -> &Subbuffer<[Self::VertexBufferData]> {
|
|
||||||
&self.vertex_buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
fn index_buffer(&self) -> Option<&Self::IndexBuffer> {
|
|
||||||
Some(&self.index_buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn vertex_count(&self) -> u32 {
|
|
||||||
self.vertex_buffer.len() as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
fn index_count(&self) -> u32 {
|
|
||||||
self.index_buffer.len() as u32
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
use std::{error::Error, sync::Arc};
|
|
||||||
|
|
||||||
use vulkano::{buffer::Subbuffer, memory::allocator::StandardMemoryAllocator};
|
|
||||||
|
|
||||||
use crate::core::render::primitives::{
|
|
||||||
AsIndexBuffer, AsRenderableMesh, AsVertexBuffer, vertex::Vertex3D,
|
|
||||||
};
|
|
||||||
|
|
||||||
const VERTICES: [Vertex3D; 4] = [
|
|
||||||
Vertex3D {
|
|
||||||
position: [-0.5, -0.5, 0.0],
|
|
||||||
uv: [0.0, 0.0],
|
|
||||||
},
|
|
||||||
Vertex3D {
|
|
||||||
position: [-0.5, 0.5, 0.0],
|
|
||||||
uv: [0.0, 1.0],
|
|
||||||
},
|
|
||||||
Vertex3D {
|
|
||||||
position: [0.5, -0.5, 0.0],
|
|
||||||
uv: [1.0, 0.0],
|
|
||||||
},
|
|
||||||
Vertex3D {
|
|
||||||
position: [0.5, 0.5, 0.0],
|
|
||||||
uv: [1.0, 1.0],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const INDICES: [u32; 6] = [0, 2, 1, 2, 3, 1];
|
|
||||||
|
|
||||||
pub struct SquareMesh {
|
|
||||||
vertex_buffer: Subbuffer<[Vertex3D]>,
|
|
||||||
index_buffer: Subbuffer<[u32]>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SquareMesh {
|
|
||||||
pub fn new(memory_allocator: &Arc<StandardMemoryAllocator>) -> Result<Self, Box<dyn Error>> {
|
|
||||||
let vertex_buffer = Vertex3D::create_vertex_buffer(memory_allocator, &VERTICES)?;
|
|
||||||
let index_buffer = u32::create_index_buffer(memory_allocator, &INDICES)?;
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
vertex_buffer,
|
|
||||||
index_buffer,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRenderableMesh for SquareMesh {
|
|
||||||
type VertexBufferData = Vertex3D;
|
|
||||||
type IndexBuffer = Subbuffer<[u32]>;
|
|
||||||
|
|
||||||
fn vertex_buffer(&self) -> &Subbuffer<[Self::VertexBufferData]> {
|
|
||||||
&self.vertex_buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
fn index_buffer(&self) -> Option<&Self::IndexBuffer> {
|
|
||||||
Some(&self.index_buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn vertex_count(&self) -> u32 {
|
|
||||||
VERTICES.len() as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
fn index_count(&self) -> u32 {
|
|
||||||
INDICES.len() as u32
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
pub mod meshes;
|
|
||||||
pub mod pipeline;
|
|
||||||
pub mod texture;
|
|
||||||
pub mod timer;
|
|
||||||
pub mod vulkan;
|
|
|
@ -1,115 +0,0 @@
|
||||||
use std::{
|
|
||||||
any::TypeId,
|
|
||||||
collections::HashMap,
|
|
||||||
error::Error,
|
|
||||||
sync::{Arc, RwLock},
|
|
||||||
};
|
|
||||||
|
|
||||||
use bevy_ecs::resource::Resource;
|
|
||||||
use vulkano::{device::Device, format::Format, pipeline::GraphicsPipeline};
|
|
||||||
|
|
||||||
use super::{GraphicsPipelineLoadFn, LoadableGraphicsPipeline};
|
|
||||||
|
|
||||||
#[derive(PartialEq, Eq)]
|
|
||||||
pub enum PipelineState {
|
|
||||||
NeedBuild,
|
|
||||||
Loaded,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Resource)]
|
|
||||||
pub struct PipelineLoader {
|
|
||||||
device: Arc<Device>,
|
|
||||||
swapchain_image_format: Format,
|
|
||||||
depth_image_format: Format,
|
|
||||||
|
|
||||||
// Arc<GraphicsPipeline> is used in internal of vulkano. It's not possible to use Arc<RwLock<Option<GraphicsPipeline>>> directly.
|
|
||||||
pipelines_index: HashMap<TypeId, usize>,
|
|
||||||
pipelines_id: Vec<TypeId>,
|
|
||||||
pipelines_load_fn: Vec<GraphicsPipelineLoadFn>,
|
|
||||||
// Only content is protected by Arc and RwLock to avoid push in pipeline_loader in multiple threads and just allow to lock each pipeline when is needed as parallel pipelines loading.
|
|
||||||
// But only the pipeline loader is allowed to load a pipeline when it's needed.
|
|
||||||
pipelines: Vec<Arc<RwLock<Option<Arc<GraphicsPipeline>>>>>,
|
|
||||||
pipelines_state: Vec<Arc<RwLock<PipelineState>>>,
|
|
||||||
pipelines_name: Vec<&'static str>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PipelineLoader {
|
|
||||||
pub fn new(
|
|
||||||
device: Arc<Device>,
|
|
||||||
swapchain_image_format: Format,
|
|
||||||
depth_image_format: Format,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
device,
|
|
||||||
swapchain_image_format,
|
|
||||||
depth_image_format,
|
|
||||||
pipelines: Vec::new(),
|
|
||||||
pipelines_load_fn: Vec::new(),
|
|
||||||
pipelines_name: Vec::new(),
|
|
||||||
pipelines_id: Vec::new(),
|
|
||||||
pipelines_state: Vec::new(),
|
|
||||||
pipelines_index: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn register<T: LoadableGraphicsPipeline + 'static>(
|
|
||||||
&mut self,
|
|
||||||
) -> Result<(), Box<dyn Error>> {
|
|
||||||
let id = TypeId::of::<T>();
|
|
||||||
self.pipelines_index.insert(id, self.pipelines.len());
|
|
||||||
self.pipelines_id.push(id);
|
|
||||||
self.pipelines_load_fn.push(T::load);
|
|
||||||
self.pipelines_name.push(T::pipeline_name());
|
|
||||||
self.pipelines_state
|
|
||||||
.push(Arc::new(RwLock::new(PipelineState::NeedBuild)));
|
|
||||||
self.pipelines.push(Arc::new(RwLock::new(None)));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_pending_pipelines(&self) -> Result<(), Box<dyn Error>> {
|
|
||||||
let iter = self
|
|
||||||
.pipelines_name
|
|
||||||
.iter()
|
|
||||||
.zip(self.pipelines.iter())
|
|
||||||
.zip(self.pipelines_load_fn.iter())
|
|
||||||
.zip(self.pipelines_state.iter())
|
|
||||||
.filter(|(_, state)| {
|
|
||||||
let state = state.read().unwrap();
|
|
||||||
*state == PipelineState::NeedBuild
|
|
||||||
});
|
|
||||||
|
|
||||||
for (((name, pipeline), load_fn), state) in iter {
|
|
||||||
let new_pipeline = load_fn(
|
|
||||||
&self.device,
|
|
||||||
self.swapchain_image_format,
|
|
||||||
self.depth_image_format,
|
|
||||||
)?;
|
|
||||||
let mut pipeline = pipeline.write().unwrap();
|
|
||||||
*pipeline = Some(new_pipeline);
|
|
||||||
let mut state = state.write().unwrap();
|
|
||||||
*state = PipelineState::Loaded;
|
|
||||||
tracing::trace!("Pipeline {name} loaded");
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mark_pipelines_as_need_build(&mut self) {
|
|
||||||
for state in self.pipelines_state.iter() {
|
|
||||||
let mut state = state.write().unwrap();
|
|
||||||
*state = PipelineState::NeedBuild;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_pipeline<T: 'static, F>(&self, f: F) -> Result<(), Box<dyn Error>>
|
|
||||||
where
|
|
||||||
F: FnOnce(&Arc<GraphicsPipeline>) -> Result<(), Box<dyn Error>>,
|
|
||||||
{
|
|
||||||
let id = TypeId::of::<T>();
|
|
||||||
let index = self.pipelines_index.get(&id).ok_or("Pipeline not found")?;
|
|
||||||
let pipeline_locker = self.pipelines[*index]
|
|
||||||
.read()
|
|
||||||
.map_err(|_| "Failed to lock pipeline")?;
|
|
||||||
let pipeline = pipeline_locker.as_ref().ok_or("Pipeline not loaded")?;
|
|
||||||
f(pipeline)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
mod loader;
|
|
||||||
use std::{error::Error, sync::Arc};
|
|
||||||
|
|
||||||
pub use loader::PipelineLoader;
|
|
||||||
use vulkano::{device::Device, format::Format, pipeline::GraphicsPipeline};
|
|
||||||
|
|
||||||
type GraphicsPipelineLoadFn =
|
|
||||||
fn(&Arc<Device>, Format, Format) -> Result<Arc<GraphicsPipeline>, Box<dyn Error>>;
|
|
||||||
|
|
||||||
pub trait LoadableGraphicsPipeline {
|
|
||||||
fn load(
|
|
||||||
device: &Arc<Device>,
|
|
||||||
swapchain_image_format: Format,
|
|
||||||
depth_image_format: Format,
|
|
||||||
) -> Result<Arc<GraphicsPipeline>, Box<dyn Error>>;
|
|
||||||
|
|
||||||
fn pipeline_name() -> &'static str;
|
|
||||||
}
|
|
|
@ -1,133 +0,0 @@
|
||||||
use std::{collections::HashMap, error::Error, sync::Arc};
|
|
||||||
|
|
||||||
use vulkano::{
|
|
||||||
command_buffer::{
|
|
||||||
AutoCommandBufferBuilder, CommandBufferUsage, PrimaryCommandBufferAbstract,
|
|
||||||
allocator::StandardCommandBufferAllocator,
|
|
||||||
},
|
|
||||||
device::{Device, Queue},
|
|
||||||
format::Format,
|
|
||||||
image::sampler::SamplerCreateInfo,
|
|
||||||
memory::allocator::StandardMemoryAllocator,
|
|
||||||
};
|
|
||||||
|
|
||||||
use bevy_ecs::resource::Resource;
|
|
||||||
|
|
||||||
use super::Texture;
|
|
||||||
|
|
||||||
pub enum TextureSourceKind {
|
|
||||||
File(String),
|
|
||||||
Buffer(Vec<u8>),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TextureLoadInfo {
|
|
||||||
pub source: TextureSourceKind,
|
|
||||||
pub sampler_create_info: SamplerCreateInfo,
|
|
||||||
pub image_format: Format,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Resource)]
|
|
||||||
pub struct TextureLoader {
|
|
||||||
loaded_textures: HashMap<String, Arc<Texture>>,
|
|
||||||
pending_textures: HashMap<String, TextureLoadInfo>,
|
|
||||||
device: Arc<Device>,
|
|
||||||
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
|
|
||||||
memory_allocator: Arc<StandardMemoryAllocator>,
|
|
||||||
queue: Arc<Queue>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TextureLoader {
|
|
||||||
pub fn new(
|
|
||||||
device: Arc<Device>,
|
|
||||||
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
|
|
||||||
memory_allocator: Arc<StandardMemoryAllocator>,
|
|
||||||
queue: Arc<Queue>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
loaded_textures: HashMap::new(),
|
|
||||||
pending_textures: HashMap::new(),
|
|
||||||
device,
|
|
||||||
command_buffer_allocator,
|
|
||||||
memory_allocator,
|
|
||||||
queue,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn select_best_suitable_queue(
|
|
||||||
graphics_queue: &Arc<Queue>,
|
|
||||||
transfer_queue: Option<&Arc<Queue>>,
|
|
||||||
) -> Arc<Queue> {
|
|
||||||
transfer_queue
|
|
||||||
.map(|queue| {
|
|
||||||
tracing::trace!(
|
|
||||||
"Select transfer queue for texture loading with family index: {:?}",
|
|
||||||
queue.queue_family_index()
|
|
||||||
);
|
|
||||||
queue.clone()
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|| {
|
|
||||||
tracing::trace!(
|
|
||||||
"Select graphics queue for texture loading with family index: {:?}",
|
|
||||||
graphics_queue.queue_family_index()
|
|
||||||
);
|
|
||||||
graphics_queue.clone()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_texture(&mut self, name: String, load_info: TextureLoadInfo) {
|
|
||||||
self.pending_textures.insert(name, load_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_pending_textures(&mut self) -> Result<(), Box<dyn Error>> {
|
|
||||||
let _span = tracing::info_span!("load_pending_textures");
|
|
||||||
let mut uploads = AutoCommandBufferBuilder::primary(
|
|
||||||
self.command_buffer_allocator.clone(),
|
|
||||||
self.queue.queue_family_index(),
|
|
||||||
CommandBufferUsage::OneTimeSubmit,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let mut loading_textures = HashMap::new();
|
|
||||||
|
|
||||||
tracing::trace!("Pending textures count: {}", self.pending_textures.len());
|
|
||||||
|
|
||||||
for (name, info) in self.pending_textures.iter() {
|
|
||||||
let texture = match &info.source {
|
|
||||||
TextureSourceKind::File(path) => Texture::from_file(
|
|
||||||
&self.device,
|
|
||||||
&self.memory_allocator,
|
|
||||||
&mut uploads,
|
|
||||||
path.as_str(),
|
|
||||||
info,
|
|
||||||
)?,
|
|
||||||
TextureSourceKind::Buffer(buffer) => Texture::from_bytes(
|
|
||||||
&self.device,
|
|
||||||
&self.memory_allocator,
|
|
||||||
&mut uploads,
|
|
||||||
&buffer,
|
|
||||||
info,
|
|
||||||
)?,
|
|
||||||
};
|
|
||||||
|
|
||||||
loading_textures.insert(name.clone(), Arc::new(texture));
|
|
||||||
tracing::trace!("Loaded texture: {}", name);
|
|
||||||
}
|
|
||||||
|
|
||||||
let _ = uploads.build()?.execute(self.queue.clone())?;
|
|
||||||
|
|
||||||
self.loaded_textures.extend(loading_textures);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_texture(&self, name: &str) -> Option<&Arc<Texture>> {
|
|
||||||
self.loaded_textures.get(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pending_textures_count(&self) -> usize {
|
|
||||||
self.pending_textures.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn loaded_textures_count(&self) -> usize {
|
|
||||||
self.loaded_textures.len()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
mod texture;
|
|
||||||
pub use texture::Texture;
|
|
||||||
|
|
||||||
mod loader;
|
|
||||||
pub use loader::{TextureLoadInfo, TextureLoader, TextureSourceKind};
|
|
|
@ -1,152 +0,0 @@
|
||||||
use std::{collections::BTreeMap, error::Error, sync::Arc};
|
|
||||||
|
|
||||||
use image::DynamicImage;
|
|
||||||
use vulkano::{
|
|
||||||
Validated, VulkanError,
|
|
||||||
buffer::{Buffer, BufferCreateInfo, BufferUsage},
|
|
||||||
command_buffer::{AutoCommandBufferBuilder, CopyBufferToImageInfo, PrimaryAutoCommandBuffer},
|
|
||||||
descriptor_set::{
|
|
||||||
DescriptorSet, WriteDescriptorSet,
|
|
||||||
allocator::StandardDescriptorSetAllocator,
|
|
||||||
layout::{DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorType},
|
|
||||||
},
|
|
||||||
device::Device,
|
|
||||||
format::Format,
|
|
||||||
image::{Image, ImageCreateInfo, ImageType, ImageUsage, sampler::Sampler, view::ImageView},
|
|
||||||
memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator},
|
|
||||||
shader::ShaderStages,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::core::render::primitives::{AsDescriptorSet, AsDescriptorSetLayoutBindings};
|
|
||||||
|
|
||||||
use super::TextureLoadInfo;
|
|
||||||
|
|
||||||
pub struct Texture {
|
|
||||||
texture: Arc<ImageView>,
|
|
||||||
sampler: Arc<Sampler>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Texture {
|
|
||||||
fn new(texture: Arc<ImageView>, sampler: Arc<Sampler>) -> Self {
|
|
||||||
Self { texture, sampler }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn from_file(
|
|
||||||
device: &Arc<Device>,
|
|
||||||
memory_allocator: &Arc<StandardMemoryAllocator>,
|
|
||||||
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
|
|
||||||
path: &str,
|
|
||||||
load_info: &TextureLoadInfo,
|
|
||||||
) -> Result<Self, Box<dyn Error>> {
|
|
||||||
let bytes = std::fs::read(path)?;
|
|
||||||
Self::from_bytes(device, memory_allocator, builder, &bytes, load_info)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn from_bytes(
|
|
||||||
device: &Arc<Device>,
|
|
||||||
memory_allocator: &Arc<StandardMemoryAllocator>,
|
|
||||||
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
|
|
||||||
bytes: &[u8],
|
|
||||||
load_info: &TextureLoadInfo,
|
|
||||||
) -> Result<Self, Box<dyn Error>> {
|
|
||||||
let image = image::load_from_memory(bytes)?;
|
|
||||||
Self::from_dynamic_image(device, memory_allocator, builder, image, load_info)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_dynamic_image(
|
|
||||||
device: &Arc<Device>,
|
|
||||||
memory_allocator: &Arc<StandardMemoryAllocator>,
|
|
||||||
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
|
|
||||||
image: DynamicImage,
|
|
||||||
load_info: &TextureLoadInfo,
|
|
||||||
) -> Result<Self, Box<dyn Error>> {
|
|
||||||
let image_data = match load_info.image_format {
|
|
||||||
Format::R8G8B8A8_SRGB => image.to_rgba8(),
|
|
||||||
_ => return Err("Unsupported format".into()),
|
|
||||||
};
|
|
||||||
let image_dimensions = image_data.dimensions();
|
|
||||||
let image_data = image_data.into_raw();
|
|
||||||
|
|
||||||
let upload_buffer = Buffer::new_slice(
|
|
||||||
memory_allocator.clone(),
|
|
||||||
BufferCreateInfo {
|
|
||||||
usage: BufferUsage::TRANSFER_SRC,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
AllocationCreateInfo {
|
|
||||||
memory_type_filter: MemoryTypeFilter::PREFER_HOST
|
|
||||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
image_data.len() as u64,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
{
|
|
||||||
let buffer_data = &mut *upload_buffer.write()?;
|
|
||||||
buffer_data.copy_from_slice(&image_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
let image = Image::new(
|
|
||||||
memory_allocator.clone(),
|
|
||||||
ImageCreateInfo {
|
|
||||||
image_type: ImageType::Dim2d,
|
|
||||||
format: load_info.image_format,
|
|
||||||
extent: [image_dimensions.0, image_dimensions.1, 1],
|
|
||||||
array_layers: 1,
|
|
||||||
usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
AllocationCreateInfo::default(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
builder.copy_buffer_to_image(CopyBufferToImageInfo::buffer_image(
|
|
||||||
upload_buffer,
|
|
||||||
image.clone(),
|
|
||||||
))?;
|
|
||||||
|
|
||||||
let sampler = Sampler::new(device.clone(), load_info.sampler_create_info.clone())?;
|
|
||||||
|
|
||||||
let image_view = ImageView::new_default(image)?;
|
|
||||||
|
|
||||||
Ok(Self::new(image_view, sampler))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsDescriptorSetLayoutBindings for Texture {
|
|
||||||
fn as_descriptor_set_layout_bindings() -> BTreeMap<u32, DescriptorSetLayoutBinding> {
|
|
||||||
BTreeMap::<u32, DescriptorSetLayoutBinding>::from_iter([
|
|
||||||
(
|
|
||||||
0,
|
|
||||||
DescriptorSetLayoutBinding {
|
|
||||||
stages: ShaderStages::FRAGMENT,
|
|
||||||
..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::Sampler)
|
|
||||||
},
|
|
||||||
),
|
|
||||||
(
|
|
||||||
1,
|
|
||||||
DescriptorSetLayoutBinding {
|
|
||||||
stages: ShaderStages::FRAGMENT,
|
|
||||||
..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::SampledImage)
|
|
||||||
},
|
|
||||||
),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsDescriptorSet for Texture {
|
|
||||||
fn as_descriptor_set(
|
|
||||||
&self,
|
|
||||||
descriptor_set_allocator: &Arc<StandardDescriptorSetAllocator>,
|
|
||||||
layout: &Arc<DescriptorSetLayout>,
|
|
||||||
) -> Result<Arc<DescriptorSet>, Validated<VulkanError>> {
|
|
||||||
DescriptorSet::new(
|
|
||||||
descriptor_set_allocator.clone(),
|
|
||||||
layout.clone(),
|
|
||||||
[
|
|
||||||
WriteDescriptorSet::sampler(0, self.sampler.clone()),
|
|
||||||
WriteDescriptorSet::image_view(1, self.texture.clone()),
|
|
||||||
],
|
|
||||||
[],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
use bevy_ecs::resource::Resource;
|
|
||||||
|
|
||||||
#[derive(Resource)]
|
|
||||||
pub struct Timer {
|
|
||||||
start_time: std::time::Instant,
|
|
||||||
last_time: std::time::Instant,
|
|
||||||
current_time: std::time::Instant,
|
|
||||||
delta_time: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Timer {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
start_time: std::time::Instant::now(),
|
|
||||||
last_time: std::time::Instant::now(),
|
|
||||||
current_time: std::time::Instant::now(),
|
|
||||||
delta_time: 0.0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(&mut self) {
|
|
||||||
self.current_time = std::time::Instant::now();
|
|
||||||
self.delta_time = self
|
|
||||||
.current_time
|
|
||||||
.duration_since(self.last_time)
|
|
||||||
.as_secs_f32();
|
|
||||||
self.last_time = self.current_time;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn delta_time(&self) -> f32 {
|
|
||||||
self.delta_time
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn start_time(&self) -> std::time::Instant {
|
|
||||||
self.start_time
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,145 +0,0 @@
|
||||||
use bevy_ecs::prelude::Resource;
|
|
||||||
use std::{ops::Deref, sync::Arc};
|
|
||||||
use vulkano::{
|
|
||||||
command_buffer::allocator::StandardCommandBufferAllocator,
|
|
||||||
descriptor_set::allocator::StandardDescriptorSetAllocator,
|
|
||||||
device::{Device, Queue},
|
|
||||||
instance::Instance,
|
|
||||||
memory::allocator::StandardMemoryAllocator,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Resource)]
|
|
||||||
pub struct VulkanInstance(Arc<Instance>);
|
|
||||||
|
|
||||||
impl From<&Arc<Instance>> for VulkanInstance {
|
|
||||||
fn from(instance: &Arc<Instance>) -> Self {
|
|
||||||
Self(instance.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for VulkanInstance {
|
|
||||||
type Target = Arc<Instance>;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Resource)]
|
|
||||||
pub struct VulkanDevice(Arc<Device>);
|
|
||||||
|
|
||||||
impl From<&Arc<Device>> for VulkanDevice {
|
|
||||||
fn from(device: &Arc<Device>) -> Self {
|
|
||||||
Self(device.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for VulkanDevice {
|
|
||||||
type Target = Arc<Device>;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Resource)]
|
|
||||||
pub struct VulkanGraphicsQueue(Arc<Queue>);
|
|
||||||
|
|
||||||
impl From<&Arc<Queue>> for VulkanGraphicsQueue {
|
|
||||||
fn from(queue: &Arc<Queue>) -> Self {
|
|
||||||
Self(queue.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for VulkanGraphicsQueue {
|
|
||||||
type Target = Arc<Queue>;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Resource)]
|
|
||||||
pub struct VulkanComputeQueue(Arc<Queue>);
|
|
||||||
|
|
||||||
impl From<&Arc<Queue>> for VulkanComputeQueue {
|
|
||||||
fn from(queue: &Arc<Queue>) -> Self {
|
|
||||||
Self(queue.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for VulkanComputeQueue {
|
|
||||||
type Target = Arc<Queue>;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Resource)]
|
|
||||||
pub struct VulkanTransferQueue(Option<Arc<Queue>>);
|
|
||||||
|
|
||||||
impl From<Option<&Arc<Queue>>> for VulkanTransferQueue {
|
|
||||||
fn from(queue: Option<&Arc<Queue>>) -> Self {
|
|
||||||
Self(queue.cloned())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for VulkanTransferQueue {
|
|
||||||
type Target = Option<Arc<Queue>>;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Resource)]
|
|
||||||
pub struct VulkanMemoryAllocator(Arc<StandardMemoryAllocator>);
|
|
||||||
|
|
||||||
impl From<&Arc<StandardMemoryAllocator>> for VulkanMemoryAllocator {
|
|
||||||
fn from(allocator: &Arc<StandardMemoryAllocator>) -> Self {
|
|
||||||
Self(allocator.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for VulkanMemoryAllocator {
|
|
||||||
type Target = Arc<StandardMemoryAllocator>;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Resource)]
|
|
||||||
pub struct VulkanCommandBufferAllocator(Arc<StandardCommandBufferAllocator>);
|
|
||||||
|
|
||||||
impl From<&Arc<StandardCommandBufferAllocator>> for VulkanCommandBufferAllocator {
|
|
||||||
fn from(allocator: &Arc<StandardCommandBufferAllocator>) -> Self {
|
|
||||||
Self(allocator.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for VulkanCommandBufferAllocator {
|
|
||||||
type Target = Arc<StandardCommandBufferAllocator>;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Resource)]
|
|
||||||
pub struct VulkanDescriptorSetAllocator(Arc<StandardDescriptorSetAllocator>);
|
|
||||||
|
|
||||||
impl From<&Arc<StandardDescriptorSetAllocator>> for VulkanDescriptorSetAllocator {
|
|
||||||
fn from(allocator: &Arc<StandardDescriptorSetAllocator>) -> Self {
|
|
||||||
Self(allocator.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for VulkanDescriptorSetAllocator {
|
|
||||||
type Target = Arc<StandardDescriptorSetAllocator>;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use vulkano::{
|
|
||||||
command_buffer::allocator::StandardCommandBufferAllocator,
|
|
||||||
descriptor_set::allocator::StandardDescriptorSetAllocator,
|
|
||||||
};
|
|
||||||
use vulkano_util::context::VulkanoContext;
|
|
||||||
|
|
||||||
pub struct VulkanContext {
|
|
||||||
vulkano_context: VulkanoContext,
|
|
||||||
command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
|
|
||||||
descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VulkanContext {
|
|
||||||
pub fn new(vulkano_context: VulkanoContext) -> Self {
|
|
||||||
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
|
|
||||||
vulkano_context.device().clone(),
|
|
||||||
Default::default(),
|
|
||||||
));
|
|
||||||
|
|
||||||
let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
|
|
||||||
vulkano_context.device().clone(),
|
|
||||||
Default::default(),
|
|
||||||
));
|
|
||||||
|
|
||||||
Self {
|
|
||||||
vulkano_context,
|
|
||||||
command_buffer_allocator,
|
|
||||||
descriptor_set_allocator,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn vulkano_context(&self) -> &VulkanoContext {
|
|
||||||
&self.vulkano_context
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn command_buffer_allocator(&self) -> &Arc<StandardCommandBufferAllocator> {
|
|
||||||
&self.command_buffer_allocator
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn descriptor_set_allocator(&self) -> &Arc<StandardDescriptorSetAllocator> {
|
|
||||||
&self.descriptor_set_allocator
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,81 +0,0 @@
|
||||||
use bevy_ecs::world::World;
|
|
||||||
|
|
||||||
use crate::core::{
|
|
||||||
app::{DEPTH_IMAGE_ID, context::WindowContext},
|
|
||||||
render::{
|
|
||||||
resources::vulkan::{
|
|
||||||
VulkanCommandBufferAllocator, VulkanComputeQueue, VulkanDescriptorSetAllocator,
|
|
||||||
VulkanDevice, VulkanGraphicsQueue, VulkanInstance, VulkanMemoryAllocator,
|
|
||||||
VulkanTransferQueue,
|
|
||||||
},
|
|
||||||
resources::{pipeline::PipelineLoader, texture::TextureLoader},
|
|
||||||
vulkan_context::VulkanContext,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn extract_vulkan_ressources(world: &mut World, vulkan_context: &VulkanContext) {
|
|
||||||
let vulkano_context = vulkan_context.vulkano_context();
|
|
||||||
|
|
||||||
let vulkan_instance = vulkano_context.instance();
|
|
||||||
let vulkan_device = vulkano_context.device();
|
|
||||||
let vulkan_graphics_queue = vulkano_context.graphics_queue();
|
|
||||||
let vulkan_compute_queue = vulkano_context.compute_queue();
|
|
||||||
let vulkan_transfer_queue = vulkano_context.transfer_queue();
|
|
||||||
let vulkan_memory_allocator = vulkano_context.memory_allocator();
|
|
||||||
let vulkan_command_buffer_allocator = vulkan_context.command_buffer_allocator();
|
|
||||||
let vulkan_descriptor_set_allocator = vulkan_context.descriptor_set_allocator();
|
|
||||||
|
|
||||||
world.insert_resource(VulkanInstance::from(vulkan_instance));
|
|
||||||
world.insert_resource(VulkanDevice::from(vulkan_device));
|
|
||||||
world.insert_resource(VulkanGraphicsQueue::from(vulkan_graphics_queue));
|
|
||||||
world.insert_resource(VulkanComputeQueue::from(vulkan_compute_queue));
|
|
||||||
world.insert_resource(VulkanTransferQueue::from(vulkan_transfer_queue));
|
|
||||||
world.insert_resource(VulkanMemoryAllocator::from(vulkan_memory_allocator));
|
|
||||||
world.insert_resource(VulkanCommandBufferAllocator::from(
|
|
||||||
vulkan_command_buffer_allocator,
|
|
||||||
));
|
|
||||||
world.insert_resource(VulkanDescriptorSetAllocator::from(
|
|
||||||
vulkan_descriptor_set_allocator,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn extract_texture_loader(world: &mut World, vulkan_context: &VulkanContext) {
|
|
||||||
let vulkano_context = vulkan_context.vulkano_context();
|
|
||||||
|
|
||||||
let vulkan_device = vulkano_context.device();
|
|
||||||
let vulkan_command_buffer_allocator = vulkan_context.command_buffer_allocator();
|
|
||||||
let vulkan_memory_allocator = vulkano_context.memory_allocator();
|
|
||||||
let vulkan_graphics_queue = vulkano_context.graphics_queue();
|
|
||||||
let vulkan_transfer_queue = vulkano_context.transfer_queue();
|
|
||||||
|
|
||||||
let texture_loader = TextureLoader::new(
|
|
||||||
vulkan_device.clone(),
|
|
||||||
vulkan_command_buffer_allocator.clone(),
|
|
||||||
vulkan_memory_allocator.clone(),
|
|
||||||
TextureLoader::select_best_suitable_queue(vulkan_graphics_queue, vulkan_transfer_queue),
|
|
||||||
);
|
|
||||||
|
|
||||||
world.insert_resource(texture_loader);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn extract_pipeline_loader(world: &mut World, window_context: &mut WindowContext) {
|
|
||||||
let vulkan_device = window_context
|
|
||||||
.vulkan_context()
|
|
||||||
.vulkano_context()
|
|
||||||
.device()
|
|
||||||
.clone();
|
|
||||||
|
|
||||||
let depth_image_view = window_context
|
|
||||||
.with_renderer_mut(|renderer| renderer.get_additional_image_view(DEPTH_IMAGE_ID).clone());
|
|
||||||
|
|
||||||
let swapchain_image_view =
|
|
||||||
window_context.with_renderer(|renderer| renderer.swapchain_image_view().clone());
|
|
||||||
|
|
||||||
let pipeline_loader = PipelineLoader::new(
|
|
||||||
vulkan_device.clone(),
|
|
||||||
swapchain_image_view.format(),
|
|
||||||
depth_image_view.format(),
|
|
||||||
);
|
|
||||||
|
|
||||||
world.insert_resource(pipeline_loader);
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
use std::error::Error;
|
|
||||||
|
|
||||||
use crate::core::app::context::WindowContext;
|
|
||||||
use crate::core::input::InputManagerResource;
|
|
||||||
use crate::core::scene::AsScene;
|
|
||||||
use crate::core::scene::extract::{
|
|
||||||
extract_pipeline_loader, extract_texture_loader, extract_vulkan_ressources,
|
|
||||||
};
|
|
||||||
use bevy_ecs::world::World;
|
|
||||||
|
|
||||||
use super::Scene;
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct SceneManager {
|
|
||||||
current_scene: Option<Scene>,
|
|
||||||
new_scene: Option<Box<dyn AsScene>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SceneManager {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
current_scene: None,
|
|
||||||
new_scene: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_world_with_resources(window_context: &mut WindowContext) -> World {
|
|
||||||
let mut world = World::new();
|
|
||||||
|
|
||||||
extract_vulkan_ressources(&mut world, window_context.vulkan_context());
|
|
||||||
extract_texture_loader(&mut world, window_context.vulkan_context());
|
|
||||||
extract_pipeline_loader(&mut world, window_context);
|
|
||||||
world.insert_resource(InputManagerResource(window_context.input_manager.clone()));
|
|
||||||
|
|
||||||
world
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_new_scene(&mut self, scene_impl: Box<dyn AsScene>) {
|
|
||||||
self.new_scene = Some(scene_impl);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn current_scene(&self) -> Option<&Scene> {
|
|
||||||
self.current_scene.as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn current_scene_mut(&mut self) -> Option<&mut Scene> {
|
|
||||||
self.current_scene.as_mut()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_scene_if_not_loaded(
|
|
||||||
&mut self,
|
|
||||||
window_context: &mut WindowContext,
|
|
||||||
) -> Result<(), Box<dyn Error>> {
|
|
||||||
if let Some(new_scene) = self.new_scene.take() {
|
|
||||||
let world = Self::create_world_with_resources(window_context);
|
|
||||||
let mut scene = Scene::new(new_scene, world);
|
|
||||||
scene.load(window_context)?;
|
|
||||||
|
|
||||||
if let Some(mut current_scene) = self.current_scene.take() {
|
|
||||||
current_scene.unload();
|
|
||||||
}
|
|
||||||
|
|
||||||
self.current_scene = Some(scene);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,74 +0,0 @@
|
||||||
use std::error::Error;
|
|
||||||
|
|
||||||
use bevy_ecs::{schedule::Schedule, world::World};
|
|
||||||
use vulkano::sync::GpuFuture;
|
|
||||||
|
|
||||||
use crate::core::{app::context::WindowContext, scene::schedule::SceneScedule};
|
|
||||||
|
|
||||||
mod extract;
|
|
||||||
pub mod manager;
|
|
||||||
pub mod schedule;
|
|
||||||
|
|
||||||
/// Structure Scene qui contient le world et l'implémentation AsScene
|
|
||||||
pub struct Scene {
|
|
||||||
pub world: World,
|
|
||||||
pub loop_schedule: Schedule,
|
|
||||||
pub implementation: Box<dyn AsScene>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Scene {
|
|
||||||
pub fn new(implementation: Box<dyn AsScene>, world: World) -> Self {
|
|
||||||
Self {
|
|
||||||
world,
|
|
||||||
loop_schedule: SceneScedule::base_schedule(),
|
|
||||||
implementation,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn loaded(&self) -> bool {
|
|
||||||
self.implementation.loaded()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load(&mut self, window_context: &mut WindowContext) -> Result<(), Box<dyn Error>> {
|
|
||||||
self.implementation.load(&mut self.world, window_context)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(&mut self, window_context: &WindowContext) -> Result<(), Box<dyn Error>> {
|
|
||||||
self.implementation.update(&mut self.world, window_context)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render(
|
|
||||||
&mut self,
|
|
||||||
acquire_future: Box<dyn GpuFuture>,
|
|
||||||
window_context: &mut WindowContext,
|
|
||||||
) -> Result<Box<dyn GpuFuture>, Box<dyn Error>> {
|
|
||||||
self.implementation
|
|
||||||
.render(acquire_future, &mut self.world, window_context)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unload(&mut self) {
|
|
||||||
self.implementation.unload()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Trait pour les implémentations de scènes
|
|
||||||
pub trait AsScene {
|
|
||||||
fn loaded(&self) -> bool;
|
|
||||||
fn load(
|
|
||||||
&mut self,
|
|
||||||
world: &mut World,
|
|
||||||
window_context: &mut WindowContext,
|
|
||||||
) -> Result<(), Box<dyn Error>>;
|
|
||||||
fn update(
|
|
||||||
&mut self,
|
|
||||||
world: &mut World,
|
|
||||||
window_context: &WindowContext,
|
|
||||||
) -> Result<(), Box<dyn Error>>;
|
|
||||||
fn render(
|
|
||||||
&mut self,
|
|
||||||
acquire_future: Box<dyn GpuFuture>,
|
|
||||||
world: &mut World,
|
|
||||||
window_context: &mut WindowContext,
|
|
||||||
) -> Result<Box<dyn GpuFuture>, Box<dyn Error>>;
|
|
||||||
fn unload(&mut self);
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
use bevy_ecs::schedule::{IntoScheduleConfigs, Schedule, ScheduleLabel, SystemSet};
|
|
||||||
|
|
||||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
|
|
||||||
pub enum SceneSystems {
|
|
||||||
ExtractViews,
|
|
||||||
Update,
|
|
||||||
Render,
|
|
||||||
Present,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(ScheduleLabel, Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
||||||
pub struct SceneScedule;
|
|
||||||
|
|
||||||
impl SceneScedule {
|
|
||||||
pub fn base_schedule() -> Schedule {
|
|
||||||
use SceneSystems::*;
|
|
||||||
|
|
||||||
let mut schedule = Schedule::new(Self);
|
|
||||||
|
|
||||||
schedule.configure_sets((ExtractViews, Update, Render, Present).chain());
|
|
||||||
|
|
||||||
schedule
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
pub mod pipelines;
|
|
|
@ -1 +0,0 @@
|
||||||
pub mod simple;
|
|
|
@ -1,178 +0,0 @@
|
||||||
use std::{error::Error, sync::Arc};
|
|
||||||
|
|
||||||
use vulkano::{
|
|
||||||
command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer},
|
|
||||||
descriptor_set::{
|
|
||||||
allocator::StandardDescriptorSetAllocator, layout::DescriptorSetLayoutCreateInfo,
|
|
||||||
},
|
|
||||||
device::Device,
|
|
||||||
format::Format,
|
|
||||||
pipeline::{
|
|
||||||
DynamicState, GraphicsPipeline, Pipeline, PipelineBindPoint, PipelineLayout,
|
|
||||||
PipelineShaderStageCreateInfo,
|
|
||||||
graphics::{
|
|
||||||
GraphicsPipelineCreateInfo,
|
|
||||||
color_blend::{ColorBlendAttachmentState, ColorBlendState},
|
|
||||||
depth_stencil::{DepthState, DepthStencilState},
|
|
||||||
input_assembly::InputAssemblyState,
|
|
||||||
multisample::MultisampleState,
|
|
||||||
rasterization::RasterizationState,
|
|
||||||
subpass::PipelineRenderingCreateInfo,
|
|
||||||
vertex_input::{Vertex, VertexDefinition},
|
|
||||||
viewport::ViewportState,
|
|
||||||
},
|
|
||||||
layout::{PipelineDescriptorSetLayoutCreateInfo, PipelineLayoutCreateFlags},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::core::render::{
|
|
||||||
primitives::{
|
|
||||||
AsDescriptorSet, AsDescriptorSetLayoutBindings, AsRecordable, AsRenderableMesh,
|
|
||||||
AsRenderableMeshInstance, mvp::Mvp, transform::TransformRaw, vertex::Vertex3D,
|
|
||||||
},
|
|
||||||
resources::{pipeline::LoadableGraphicsPipeline, texture::Texture},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct SimplePipeline;
|
|
||||||
|
|
||||||
impl LoadableGraphicsPipeline for SimplePipeline {
|
|
||||||
fn load(
|
|
||||||
device: &Arc<Device>,
|
|
||||||
swapchain_format: Format,
|
|
||||||
depth_format: Format,
|
|
||||||
) -> Result<Arc<GraphicsPipeline>, Box<dyn Error>> {
|
|
||||||
let vs = shaders::vs::load(device.clone())?
|
|
||||||
.entry_point("main")
|
|
||||||
.ok_or("Failed find main entry point of vertex shader".to_string())?;
|
|
||||||
|
|
||||||
let fs = shaders::fs::load(device.clone())?
|
|
||||||
.entry_point("main")
|
|
||||||
.ok_or("Failed find main entry point of fragment shader".to_string())?;
|
|
||||||
|
|
||||||
let vertex_input_state =
|
|
||||||
[Vertex3D::per_vertex(), TransformRaw::per_instance()].definition(&vs)?;
|
|
||||||
|
|
||||||
let stages = [
|
|
||||||
PipelineShaderStageCreateInfo::new(vs),
|
|
||||||
PipelineShaderStageCreateInfo::new(fs),
|
|
||||||
];
|
|
||||||
|
|
||||||
let mvp_bindings = Mvp::as_descriptor_set_layout_bindings();
|
|
||||||
let texture_bindings = Texture::as_descriptor_set_layout_bindings();
|
|
||||||
|
|
||||||
let vertex_descriptor_set_layout = DescriptorSetLayoutCreateInfo {
|
|
||||||
bindings: mvp_bindings,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let fragment_descriptor_set_layout = DescriptorSetLayoutCreateInfo {
|
|
||||||
bindings: texture_bindings,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let create_info = PipelineDescriptorSetLayoutCreateInfo {
|
|
||||||
set_layouts: vec![vertex_descriptor_set_layout, fragment_descriptor_set_layout],
|
|
||||||
flags: PipelineLayoutCreateFlags::default(),
|
|
||||||
push_constant_ranges: vec![],
|
|
||||||
}
|
|
||||||
.into_pipeline_layout_create_info(device.clone())?;
|
|
||||||
|
|
||||||
let layout = PipelineLayout::new(device.clone(), create_info)?;
|
|
||||||
|
|
||||||
let subpass = PipelineRenderingCreateInfo {
|
|
||||||
color_attachment_formats: vec![Some(swapchain_format)],
|
|
||||||
depth_attachment_format: Some(depth_format),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let pipeline = GraphicsPipeline::new(
|
|
||||||
device.clone(),
|
|
||||||
None,
|
|
||||||
GraphicsPipelineCreateInfo {
|
|
||||||
stages: stages.into_iter().collect(),
|
|
||||||
vertex_input_state: Some(vertex_input_state),
|
|
||||||
input_assembly_state: Some(InputAssemblyState::default()),
|
|
||||||
viewport_state: Some(ViewportState::default()),
|
|
||||||
rasterization_state: Some(RasterizationState::default()),
|
|
||||||
multisample_state: Some(MultisampleState::default()),
|
|
||||||
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
|
||||||
subpass.color_attachment_formats.len() as u32,
|
|
||||||
ColorBlendAttachmentState::default(),
|
|
||||||
)),
|
|
||||||
depth_stencil_state: Some(DepthStencilState {
|
|
||||||
depth: Some(DepthState::simple()),
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
|
|
||||||
subpass: Some(subpass.into()),
|
|
||||||
..GraphicsPipelineCreateInfo::layout(layout)
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(pipeline)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pipeline_name() -> &'static str {
|
|
||||||
"SimplePipeline"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRecordable for SimplePipeline {
|
|
||||||
fn record_bind_commands(
|
|
||||||
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
|
|
||||||
descriptor_set_allocator: &Arc<StandardDescriptorSetAllocator>,
|
|
||||||
pipeline: &Arc<GraphicsPipeline>,
|
|
||||||
mesh: &impl AsRenderableMesh,
|
|
||||||
instances: &impl AsRenderableMeshInstance,
|
|
||||||
descriptor_sets: Vec<Arc<dyn AsDescriptorSet>>,
|
|
||||||
) -> Result<(), Box<dyn Error>> {
|
|
||||||
builder.bind_pipeline_graphics(pipeline.clone())?;
|
|
||||||
|
|
||||||
if !descriptor_sets.is_empty() {
|
|
||||||
let layouts = pipeline.layout().set_layouts();
|
|
||||||
|
|
||||||
let descriptor_sets = descriptor_sets
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(layout_index, data)| {
|
|
||||||
data.as_descriptor_set(descriptor_set_allocator, &layouts[layout_index])
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
|
||||||
|
|
||||||
builder.bind_descriptor_sets(
|
|
||||||
PipelineBindPoint::Graphics,
|
|
||||||
pipeline.layout().clone(),
|
|
||||||
0,
|
|
||||||
descriptor_sets,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.bind_vertex_buffers(
|
|
||||||
0,
|
|
||||||
(
|
|
||||||
mesh.vertex_buffer().clone(),
|
|
||||||
instances.instance_buffer().clone(),
|
|
||||||
),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod shaders {
|
|
||||||
pub mod vs {
|
|
||||||
vulkano_shaders::shader! {
|
|
||||||
ty: "vertex",
|
|
||||||
path: r"res/shaders/simple.vert",
|
|
||||||
generate_structs: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod fs {
|
|
||||||
vulkano_shaders::shader! {
|
|
||||||
ty: "fragment",
|
|
||||||
path: r"res/shaders/simple.frag",
|
|
||||||
generate_structs: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,2 +1,36 @@
|
||||||
pub mod assets;
|
use bevy_app::App;
|
||||||
pub mod scenes;
|
use engine_render::RenderPlugin;
|
||||||
|
use engine_vulkan::{VulkanConfig, VulkanPlugin};
|
||||||
|
use engine_window::{WindowPlugin, config::WindowConfig};
|
||||||
|
use vulkano::device::{DeviceExtensions, DeviceFeatures};
|
||||||
|
|
||||||
|
pub fn init(app: &mut App) {
|
||||||
|
let window_config = WindowConfig {
|
||||||
|
title: "Rust ASH Test".to_string(),
|
||||||
|
width: 800,
|
||||||
|
height: 600,
|
||||||
|
};
|
||||||
|
|
||||||
|
let device_extensions = DeviceExtensions {
|
||||||
|
khr_dynamic_rendering: true,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let device_features = DeviceFeatures {
|
||||||
|
dynamic_rendering: true,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let vulkan_config = VulkanConfig {
|
||||||
|
instance_layers: vec![String::from("VK_LAYER_KHRONOS_validation")],
|
||||||
|
device_extensions,
|
||||||
|
device_features,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
app.add_plugins((
|
||||||
|
WindowPlugin { window_config },
|
||||||
|
VulkanPlugin { vulkan_config },
|
||||||
|
RenderPlugin,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
|
@ -1,491 +0,0 @@
|
||||||
use std::error::Error;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use super::settings_scene::SettingsScene;
|
|
||||||
use crate::core::app::DEPTH_IMAGE_ID;
|
|
||||||
use crate::core::app::context::WindowContext;
|
|
||||||
use crate::core::app::user_event::UserEvent;
|
|
||||||
use crate::core::input::InputManagerResource;
|
|
||||||
use crate::core::render::primitives::camera::{Camera3D, Camera3DTransform};
|
|
||||||
use crate::core::render::primitives::transform::Transform;
|
|
||||||
use crate::core::render::primitives::velocity::Velocity;
|
|
||||||
use crate::core::render::primitives::{AsDescriptorSet, AsRecordable};
|
|
||||||
use crate::core::render::render_pass_manager::{RenderPassConfig, RenderPassManager};
|
|
||||||
use crate::core::render::resources::meshes::{ObjMesh, SquareMesh};
|
|
||||||
use crate::core::render::resources::pipeline::PipelineLoader;
|
|
||||||
use crate::core::render::resources::texture::{TextureLoadInfo, TextureLoader, TextureSourceKind};
|
|
||||||
use crate::core::render::resources::timer::Timer;
|
|
||||||
use crate::core::render::resources::vulkan::{
|
|
||||||
VulkanCommandBufferAllocator, VulkanDescriptorSetAllocator, VulkanGraphicsQueue,
|
|
||||||
VulkanMemoryAllocator,
|
|
||||||
};
|
|
||||||
use crate::core::scene::AsScene;
|
|
||||||
use crate::game::assets::pipelines::simple::SimplePipeline;
|
|
||||||
use bevy_ecs::prelude::*;
|
|
||||||
use bevy_ecs::schedule::Schedule;
|
|
||||||
use bevy_ecs::world::World;
|
|
||||||
use egui_winit_vulkano::egui;
|
|
||||||
use glam::EulerRot;
|
|
||||||
use glam::Quat;
|
|
||||||
use glam::Vec3;
|
|
||||||
use vulkano::format::Format;
|
|
||||||
use vulkano::image::sampler::{Filter, SamplerAddressMode, SamplerCreateInfo};
|
|
||||||
use vulkano::{
|
|
||||||
command_buffer::{AutoCommandBufferBuilder, CommandBufferUsage},
|
|
||||||
sync::GpuFuture,
|
|
||||||
};
|
|
||||||
use winit::window::CursorGrabMode;
|
|
||||||
|
|
||||||
#[derive(Component)]
|
|
||||||
pub struct Square;
|
|
||||||
|
|
||||||
#[derive(Component)]
|
|
||||||
pub struct Cube;
|
|
||||||
|
|
||||||
pub struct MainSceneState {
|
|
||||||
square: SquareMesh,
|
|
||||||
obj: ObjMesh,
|
|
||||||
scheduler: Schedule,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Resource)]
|
|
||||||
pub struct MovementSpeed(f32);
|
|
||||||
|
|
||||||
#[derive(Resource)]
|
|
||||||
pub struct CameraSensitivity(f32);
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct MainScene {
|
|
||||||
state: Option<MainSceneState>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsScene for MainScene {
|
|
||||||
fn loaded(&self) -> bool {
|
|
||||||
self.state.is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load(
|
|
||||||
&mut self,
|
|
||||||
world: &mut World,
|
|
||||||
window_context: &mut WindowContext,
|
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let mut pipeline_loader = world.resource_mut::<PipelineLoader>();
|
|
||||||
pipeline_loader.register::<SimplePipeline>()?;
|
|
||||||
pipeline_loader.load_pending_pipelines()?;
|
|
||||||
|
|
||||||
let mut texture_loader = world.resource_mut::<TextureLoader>();
|
|
||||||
texture_loader.add_texture(
|
|
||||||
"wooden-crate".to_string(),
|
|
||||||
TextureLoadInfo {
|
|
||||||
source: TextureSourceKind::File("res/textures/wooden-crate.jpg".to_string()),
|
|
||||||
sampler_create_info: SamplerCreateInfo {
|
|
||||||
mag_filter: Filter::Linear,
|
|
||||||
min_filter: Filter::Linear,
|
|
||||||
address_mode: [SamplerAddressMode::Repeat; 3],
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
image_format: Format::R8G8B8A8_SRGB,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
texture_loader.add_texture(
|
|
||||||
"cube-diffuse".to_string(),
|
|
||||||
TextureLoadInfo {
|
|
||||||
source: TextureSourceKind::File("res/objects/cube-diffuse.jpg".to_string()),
|
|
||||||
sampler_create_info: SamplerCreateInfo {
|
|
||||||
mag_filter: Filter::Linear,
|
|
||||||
min_filter: Filter::Linear,
|
|
||||||
address_mode: [SamplerAddressMode::Repeat; 3],
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
image_format: Format::R8G8B8A8_SRGB,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
texture_loader.load_pending_textures()?;
|
|
||||||
|
|
||||||
let square = SquareMesh::new(world.resource::<VulkanMemoryAllocator>())?;
|
|
||||||
|
|
||||||
let obj = {
|
|
||||||
let obj = ObjMesh::new(
|
|
||||||
world.resource::<VulkanMemoryAllocator>(),
|
|
||||||
"res/objects/cube.obj",
|
|
||||||
)?;
|
|
||||||
obj.into_iter().next().unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
world.spawn((
|
|
||||||
Camera3D::new(
|
|
||||||
window_context.get_aspect_ratio(),
|
|
||||||
std::f32::consts::FRAC_PI_2,
|
|
||||||
0.01,
|
|
||||||
1000.0,
|
|
||||||
),
|
|
||||||
Camera3DTransform {
|
|
||||||
position: Vec3::ZERO,
|
|
||||||
rotation: Vec3::ZERO,
|
|
||||||
},
|
|
||||||
));
|
|
||||||
|
|
||||||
world.insert_resource(MovementSpeed(50.0));
|
|
||||||
world.insert_resource(CameraSensitivity(10.0));
|
|
||||||
|
|
||||||
let mut scheduler = Schedule::default();
|
|
||||||
scheduler.add_systems(
|
|
||||||
(
|
|
||||||
update_camera_system,
|
|
||||||
update_velocity_system,
|
|
||||||
update_timer_system,
|
|
||||||
)
|
|
||||||
.chain(),
|
|
||||||
);
|
|
||||||
world.insert_resource(Timer::new());
|
|
||||||
create_entities(world, 100, 10.0, 10.0);
|
|
||||||
|
|
||||||
self.state = Some(MainSceneState {
|
|
||||||
square,
|
|
||||||
obj,
|
|
||||||
scheduler,
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(
|
|
||||||
&mut self,
|
|
||||||
world: &mut World,
|
|
||||||
window_context: &WindowContext,
|
|
||||||
) -> Result<(), Box<dyn Error>> {
|
|
||||||
let state = self.state.as_mut().unwrap();
|
|
||||||
|
|
||||||
{
|
|
||||||
let mut camera = world.query::<&mut Camera3D>().single_mut(world).unwrap();
|
|
||||||
camera.update_projection(window_context.get_aspect_ratio());
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
let input_manager = world.resource::<InputManagerResource>().0.read().unwrap();
|
|
||||||
|
|
||||||
if input_manager.get_virtual_input_state("mouse_left") > 0.0 {
|
|
||||||
let _ = window_context
|
|
||||||
.event_loop_proxy
|
|
||||||
.send_event(UserEvent::CursorVisible(window_context.window_id, false));
|
|
||||||
let _ = window_context
|
|
||||||
.event_loop_proxy
|
|
||||||
.send_event(UserEvent::CursorGrabMode(
|
|
||||||
window_context.window_id,
|
|
||||||
CursorGrabMode::Locked,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if input_manager.get_virtual_input_state("mouse_right") > 0.0 {
|
|
||||||
let _ = window_context
|
|
||||||
.event_loop_proxy
|
|
||||||
.send_event(UserEvent::CursorVisible(window_context.window_id, true));
|
|
||||||
let _ = window_context
|
|
||||||
.event_loop_proxy
|
|
||||||
.send_event(UserEvent::CursorGrabMode(
|
|
||||||
window_context.window_id,
|
|
||||||
CursorGrabMode::None,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
state.scheduler.run(world);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(
|
|
||||||
&mut self,
|
|
||||||
before_future: Box<dyn GpuFuture>,
|
|
||||||
world: &mut World,
|
|
||||||
window_context: &mut WindowContext,
|
|
||||||
) -> Result<Box<dyn GpuFuture>, Box<dyn Error>> {
|
|
||||||
let state = self.state.as_mut().ok_or("State not loaded")?;
|
|
||||||
|
|
||||||
let mut builder = AutoCommandBufferBuilder::primary(
|
|
||||||
(*world.resource::<VulkanCommandBufferAllocator>()).clone(),
|
|
||||||
world.resource::<VulkanGraphicsQueue>().queue_family_index(),
|
|
||||||
CommandBufferUsage::OneTimeSubmit,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
{
|
|
||||||
let swapchain_image_view =
|
|
||||||
window_context.with_renderer(|renderer| renderer.swapchain_image_view().clone());
|
|
||||||
let depth_image_view = window_context.with_renderer_mut(|renderer| {
|
|
||||||
renderer.get_additional_image_view(DEPTH_IMAGE_ID).clone()
|
|
||||||
});
|
|
||||||
let config = RenderPassConfig::default();
|
|
||||||
RenderPassManager::begin_standard_rendering(
|
|
||||||
&mut builder,
|
|
||||||
&config,
|
|
||||||
swapchain_image_view,
|
|
||||||
Some(depth_image_view),
|
|
||||||
window_context.get_window_size(),
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create camera uniform using the actual camera
|
|
||||||
let camera_uniform = {
|
|
||||||
let result = world
|
|
||||||
.query_filtered::<(&Camera3D, &Camera3DTransform), With<Camera3D>>()
|
|
||||||
.single(&world)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Arc::new(
|
|
||||||
result
|
|
||||||
.0
|
|
||||||
.create_buffer(result.1, world.resource::<VulkanMemoryAllocator>())?,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
let square_transforms = world
|
|
||||||
.query_filtered::<&Transform, With<Square>>()
|
|
||||||
.iter(&world)
|
|
||||||
.cloned()
|
|
||||||
.collect::<Vec<Transform>>();
|
|
||||||
let square_transform_uniform = Transform::create_buffer(
|
|
||||||
world.resource::<VulkanMemoryAllocator>(),
|
|
||||||
&square_transforms,
|
|
||||||
)?;
|
|
||||||
let cube_transforms = world
|
|
||||||
.query_filtered::<&Transform, With<Cube>>()
|
|
||||||
.iter(&world)
|
|
||||||
.cloned()
|
|
||||||
.collect::<Vec<Transform>>();
|
|
||||||
let cube_transform_uniform =
|
|
||||||
Transform::create_buffer(world.resource::<VulkanMemoryAllocator>(), &cube_transforms)?;
|
|
||||||
|
|
||||||
let texture_loader = world.resource::<TextureLoader>();
|
|
||||||
let pipeline_loader = world.resource::<PipelineLoader>();
|
|
||||||
|
|
||||||
pipeline_loader.with_pipeline::<SimplePipeline, _>(|pipeline| {
|
|
||||||
SimplePipeline::record_commands(
|
|
||||||
&mut builder,
|
|
||||||
world.resource::<VulkanDescriptorSetAllocator>(),
|
|
||||||
pipeline,
|
|
||||||
&state.square,
|
|
||||||
&square_transform_uniform,
|
|
||||||
vec![
|
|
||||||
camera_uniform.clone() as Arc<dyn AsDescriptorSet>,
|
|
||||||
texture_loader.get_texture("wooden-crate").unwrap().clone(),
|
|
||||||
],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
SimplePipeline::record_commands(
|
|
||||||
&mut builder,
|
|
||||||
world.resource::<VulkanDescriptorSetAllocator>(),
|
|
||||||
pipeline,
|
|
||||||
&state.obj,
|
|
||||||
&cube_transform_uniform,
|
|
||||||
vec![
|
|
||||||
camera_uniform.clone() as Arc<dyn AsDescriptorSet>,
|
|
||||||
texture_loader.get_texture("cube-diffuse").unwrap().clone(),
|
|
||||||
],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
})?;
|
|
||||||
|
|
||||||
RenderPassManager::end_rendering(&mut builder)?;
|
|
||||||
|
|
||||||
let command_buffer = builder.build()?;
|
|
||||||
|
|
||||||
let render_future = before_future.then_execute(
|
|
||||||
(*world.resource::<VulkanGraphicsQueue>()).clone(),
|
|
||||||
command_buffer,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let swapchain_image_view =
|
|
||||||
window_context.with_renderer(|renderer| renderer.swapchain_image_view().clone());
|
|
||||||
let input_manager_status = {
|
|
||||||
let input_manager = world.resource::<InputManagerResource>().0.read().unwrap();
|
|
||||||
format!("{:#?}", input_manager)
|
|
||||||
};
|
|
||||||
let event_loop_proxy = window_context.event_loop_proxy.clone();
|
|
||||||
let delta_time = world.resource::<Timer>().delta_time();
|
|
||||||
let window_id = window_context.window_id;
|
|
||||||
let window_size = window_context.get_window_size();
|
|
||||||
let camera_transform = world
|
|
||||||
.query_filtered::<&Camera3DTransform, With<Camera3D>>()
|
|
||||||
.single(&world)
|
|
||||||
.unwrap();
|
|
||||||
let render_future = window_context.with_gui_mut(|gui| {
|
|
||||||
gui.immediate_ui(|gui| {
|
|
||||||
let ctx = gui.context();
|
|
||||||
egui::TopBottomPanel::top("top_panel").show(&ctx, |ui| {
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.heading("Vulkan Test - Moteur 3D");
|
|
||||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
|
||||||
if ui.button("Paramètres").clicked() {
|
|
||||||
let _ = event_loop_proxy.send_event(UserEvent::ChangeScene(
|
|
||||||
window_id,
|
|
||||||
Box::new(SettingsScene::default()),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
if ui.button("Quitter").clicked() {
|
|
||||||
let _ = event_loop_proxy.send_event(UserEvent::Exit(window_id));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
egui::SidePanel::left("side_panel").show(&ctx, |ui| {
|
|
||||||
ui.heading("Informations");
|
|
||||||
|
|
||||||
ui.separator();
|
|
||||||
|
|
||||||
ui.label(format!("Résolution: {:?}", window_size));
|
|
||||||
ui.label(format!("Delta Time: {:.2}ms", delta_time * 1000.0));
|
|
||||||
|
|
||||||
ui.separator();
|
|
||||||
|
|
||||||
ui.label("Position caméra:");
|
|
||||||
let position = camera_transform.position;
|
|
||||||
ui.label(format!(" X: {:.2}", position[0]));
|
|
||||||
ui.label(format!(" Y: {:.2}", position[1]));
|
|
||||||
ui.label(format!(" Z: {:.2}", position[2]));
|
|
||||||
|
|
||||||
ui.separator();
|
|
||||||
|
|
||||||
ui.label("Rotation caméra:");
|
|
||||||
let rotation = camera_transform.rotation;
|
|
||||||
ui.label(format!(" Yaw: {:.2}°", rotation.y.to_degrees()));
|
|
||||||
ui.label(format!(" Pitch: {:.2}°", rotation.x.to_degrees()));
|
|
||||||
|
|
||||||
ui.separator();
|
|
||||||
|
|
||||||
ui.label(input_manager_status);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
gui.draw_on_image(render_future, swapchain_image_view.clone())
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(Box::new(render_future))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unload(&mut self) {
|
|
||||||
self.state = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_entities(
|
|
||||||
world: &mut World,
|
|
||||||
num_instances: u32,
|
|
||||||
instance_size: f32,
|
|
||||||
instance_spacing: f32,
|
|
||||||
) {
|
|
||||||
let num_instances_per_row = (num_instances as f32 / instance_spacing).ceil() as u32;
|
|
||||||
|
|
||||||
let square_instances = (0..num_instances)
|
|
||||||
.map(|i| {
|
|
||||||
let x_index = i % num_instances_per_row;
|
|
||||||
let z_index = i / num_instances_per_row;
|
|
||||||
|
|
||||||
let transform = Transform::new(
|
|
||||||
Vec3::new(
|
|
||||||
x_index as f32 * (instance_spacing + instance_size),
|
|
||||||
0.0,
|
|
||||||
z_index as f32 * (instance_spacing + instance_size),
|
|
||||||
),
|
|
||||||
Quat::from_euler(EulerRot::XYZ, 0.0, rand::random_range(0.0..=360.0), 0.0),
|
|
||||||
Vec3::new(instance_size, instance_size, instance_size),
|
|
||||||
);
|
|
||||||
|
|
||||||
let velocity = Velocity::new(Vec3::ZERO, Vec3::new(0.0, x_index as f32, 0.0));
|
|
||||||
|
|
||||||
(Square, transform, velocity)
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
world.spawn_batch(square_instances);
|
|
||||||
|
|
||||||
let cube_instances = (0..num_instances)
|
|
||||||
.map(|i| {
|
|
||||||
let x_index = i % num_instances_per_row;
|
|
||||||
let z_index = i / num_instances_per_row;
|
|
||||||
|
|
||||||
let transform = Transform::new(
|
|
||||||
Vec3::new(
|
|
||||||
x_index as f32 * (instance_spacing + instance_size),
|
|
||||||
0.0,
|
|
||||||
z_index as f32 * (instance_spacing + instance_size) * -1.0
|
|
||||||
- instance_spacing * 2.0,
|
|
||||||
),
|
|
||||||
Quat::from_euler(EulerRot::XYZ, 0.0, rand::random_range(0.0..=360.0), 0.0),
|
|
||||||
Vec3::new(
|
|
||||||
instance_size * 0.5,
|
|
||||||
instance_size * 0.5,
|
|
||||||
instance_size * 0.5,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
let velocity = Velocity::new(Vec3::ZERO, Vec3::new(0.0, x_index as f32, 0.0));
|
|
||||||
|
|
||||||
(Cube, transform, velocity)
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
world.spawn_batch(cube_instances);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_velocity_system(mut query: Query<(&mut Transform, &Velocity)>, time: Res<Timer>) {
|
|
||||||
for (mut transform, velocity) in query.iter_mut() {
|
|
||||||
let delta_time = time.delta_time();
|
|
||||||
|
|
||||||
// Update linear position
|
|
||||||
transform.position += velocity.linear * delta_time;
|
|
||||||
|
|
||||||
// Update angular rotation
|
|
||||||
let angular_delta = velocity.angular * delta_time;
|
|
||||||
let rotation_delta = Quat::from_euler(
|
|
||||||
EulerRot::XYZ,
|
|
||||||
angular_delta.x,
|
|
||||||
angular_delta.y,
|
|
||||||
angular_delta.z,
|
|
||||||
);
|
|
||||||
transform.rotation *= rotation_delta;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_timer_system(mut timer: ResMut<Timer>) {
|
|
||||||
timer.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_camera_system(
|
|
||||||
mut query: Query<&mut Camera3DTransform, With<Camera3D>>,
|
|
||||||
input_manager: Res<InputManagerResource>,
|
|
||||||
camera_sensitivity: Res<CameraSensitivity>,
|
|
||||||
movement_speed: Res<MovementSpeed>,
|
|
||||||
time: Res<Timer>,
|
|
||||||
) {
|
|
||||||
let input_manager = input_manager.0.read().unwrap();
|
|
||||||
let mut camera_transform = query.single_mut().unwrap();
|
|
||||||
|
|
||||||
let delta_time = time.delta_time();
|
|
||||||
|
|
||||||
camera_transform.rotation += Vec3::new(
|
|
||||||
(input_manager.get_virtual_input_state("mouse_y") * camera_sensitivity.0 * delta_time)
|
|
||||||
.to_radians(),
|
|
||||||
(input_manager.get_virtual_input_state("mouse_x") * camera_sensitivity.0 * delta_time)
|
|
||||||
.to_radians(),
|
|
||||||
0.0,
|
|
||||||
);
|
|
||||||
|
|
||||||
if camera_transform.rotation.x > std::f32::consts::FRAC_PI_2 {
|
|
||||||
camera_transform.rotation.x = std::f32::consts::FRAC_PI_2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if camera_transform.rotation.x < -std::f32::consts::FRAC_PI_2 {
|
|
||||||
camera_transform.rotation.x = -std::f32::consts::FRAC_PI_2;
|
|
||||||
}
|
|
||||||
|
|
||||||
let (yaw_sin, yaw_cos) = camera_transform.rotation.y.sin_cos();
|
|
||||||
let forward = Vec3::new(yaw_cos, 0.0, yaw_sin).normalize();
|
|
||||||
let right = Vec3::new(-yaw_sin, 0.0, yaw_cos).normalize();
|
|
||||||
|
|
||||||
let tx = input_manager.get_virtual_input_state("move_right") * movement_speed.0 * delta_time;
|
|
||||||
camera_transform.position += tx * right;
|
|
||||||
|
|
||||||
let tz = input_manager.get_virtual_input_state("move_forward") * movement_speed.0 * delta_time;
|
|
||||||
camera_transform.position += tz * forward;
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
pub mod main_scene;
|
|
||||||
pub mod settings_scene;
|
|
|
@ -1,151 +0,0 @@
|
||||||
use std::error::Error;
|
|
||||||
|
|
||||||
use crate::core::app::DEPTH_IMAGE_ID;
|
|
||||||
use crate::core::app::context::WindowContext;
|
|
||||||
use crate::core::app::user_event::UserEvent;
|
|
||||||
use crate::core::render::render_pass_manager::{RenderPassConfig, RenderPassManager};
|
|
||||||
use crate::core::render::resources::vulkan::{VulkanCommandBufferAllocator, VulkanGraphicsQueue};
|
|
||||||
use crate::core::scene::AsScene;
|
|
||||||
use bevy_ecs::world::World;
|
|
||||||
use egui_winit_vulkano::egui;
|
|
||||||
use vulkano::{
|
|
||||||
command_buffer::AutoCommandBufferBuilder, command_buffer::CommandBufferUsage, sync::GpuFuture,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::main_scene::MainScene;
|
|
||||||
|
|
||||||
pub struct SettingsSceneState {
|
|
||||||
current_resolution: [f32; 2],
|
|
||||||
available_resolutions: Vec<(u32, u32)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct SettingsScene {
|
|
||||||
state: Option<SettingsSceneState>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsScene for SettingsScene {
|
|
||||||
fn loaded(&self) -> bool {
|
|
||||||
self.state.is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load(
|
|
||||||
&mut self,
|
|
||||||
_world: &mut World,
|
|
||||||
window_context: &mut WindowContext,
|
|
||||||
) -> Result<(), Box<dyn Error>> {
|
|
||||||
let current_resolution = window_context.get_window_size();
|
|
||||||
let available_resolutions = window_context.get_available_resolutions();
|
|
||||||
|
|
||||||
self.state = Some(SettingsSceneState {
|
|
||||||
current_resolution,
|
|
||||||
available_resolutions,
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(
|
|
||||||
&mut self,
|
|
||||||
_world: &mut World,
|
|
||||||
_window_context: &WindowContext,
|
|
||||||
) -> Result<(), Box<dyn Error>> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(
|
|
||||||
&mut self,
|
|
||||||
before_future: Box<dyn GpuFuture>,
|
|
||||||
world: &mut World,
|
|
||||||
window_context: &mut WindowContext,
|
|
||||||
) -> Result<Box<dyn GpuFuture>, Box<dyn Error>> {
|
|
||||||
let state = self.state.as_ref().ok_or("State not found")?;
|
|
||||||
|
|
||||||
let mut builder = AutoCommandBufferBuilder::primary(
|
|
||||||
(*world.resource::<VulkanCommandBufferAllocator>()).clone(),
|
|
||||||
world.resource::<VulkanGraphicsQueue>().queue_family_index(),
|
|
||||||
CommandBufferUsage::OneTimeSubmit,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Utiliser le RenderPassManager
|
|
||||||
{
|
|
||||||
let swapchain_image_view =
|
|
||||||
window_context.with_renderer(|renderer| renderer.swapchain_image_view().clone());
|
|
||||||
let depth_stencil_image_view = window_context.with_renderer_mut(|renderer| {
|
|
||||||
renderer.get_additional_image_view(DEPTH_IMAGE_ID).clone()
|
|
||||||
});
|
|
||||||
|
|
||||||
let config = RenderPassConfig::default();
|
|
||||||
RenderPassManager::begin_standard_rendering(
|
|
||||||
&mut builder,
|
|
||||||
&config,
|
|
||||||
swapchain_image_view,
|
|
||||||
Some(depth_stencil_image_view),
|
|
||||||
window_context.get_window_size(),
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pas de géométrie dans cette scène - juste un écran de paramètres
|
|
||||||
RenderPassManager::end_rendering(&mut builder)?;
|
|
||||||
|
|
||||||
let command_buffer = builder.build()?;
|
|
||||||
|
|
||||||
let render_future = before_future.then_execute(
|
|
||||||
(*world.resource::<VulkanGraphicsQueue>()).clone(),
|
|
||||||
command_buffer,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let swapchain_image_view =
|
|
||||||
window_context.with_renderer(|renderer| renderer.swapchain_image_view().clone());
|
|
||||||
|
|
||||||
let event_loop_proxy = window_context.event_loop_proxy.clone();
|
|
||||||
let window_id = window_context.window_id;
|
|
||||||
|
|
||||||
let render_future = window_context.with_gui_mut(|gui| {
|
|
||||||
gui.immediate_ui(|gui| {
|
|
||||||
let ctx = gui.context();
|
|
||||||
|
|
||||||
egui::CentralPanel::default().show(&ctx, |ui| {
|
|
||||||
ui.heading("Paramètres");
|
|
||||||
|
|
||||||
ui.separator();
|
|
||||||
|
|
||||||
ui.label(format!(
|
|
||||||
"Résolution actuelle: {:?}",
|
|
||||||
state.current_resolution
|
|
||||||
));
|
|
||||||
|
|
||||||
ui.separator();
|
|
||||||
ui.label("Changer la résolution:");
|
|
||||||
|
|
||||||
for &(width, height) in &state.available_resolutions {
|
|
||||||
if ui.button(format!("{}x{}", width, height)).clicked() {
|
|
||||||
let _ = event_loop_proxy.send_event(UserEvent::ChangeResolution(
|
|
||||||
window_id,
|
|
||||||
width as f32,
|
|
||||||
height as f32,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.separator();
|
|
||||||
|
|
||||||
if ui.button("Retour au jeu").clicked() {
|
|
||||||
let _ = event_loop_proxy.send_event(UserEvent::ChangeScene(
|
|
||||||
window_id,
|
|
||||||
Box::new(MainScene::default()),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
gui.draw_on_image(render_future, swapchain_image_view.clone())
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(Box::new(render_future))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unload(&mut self) {
|
|
||||||
self.state = None;
|
|
||||||
}
|
|
||||||
}
|
|
110
src/main.rs
110
src/main.rs
|
@ -1,108 +1,18 @@
|
||||||
use core::input::{AxisDirection, InputManager, VirtualBinding};
|
use winit::event_loop::{ControlFlow, EventLoop};
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use vulkano::device::{DeviceExtensions, DeviceFeatures};
|
use bevy_app::{App, AppExit};
|
||||||
use vulkano_util::context::{VulkanoConfig, VulkanoContext};
|
|
||||||
use winit::{
|
|
||||||
event::MouseButton,
|
|
||||||
event_loop::{ControlFlow, EventLoop},
|
|
||||||
keyboard::{KeyCode, PhysicalKey},
|
|
||||||
};
|
|
||||||
|
|
||||||
mod core;
|
pub mod game;
|
||||||
mod game;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
use tracing_subscriber::layer::SubscriberExt;
|
env_logger::init();
|
||||||
use tracing_subscriber::util::SubscriberInitExt;
|
|
||||||
|
|
||||||
tracing_subscriber::registry()
|
let mut app = App::default();
|
||||||
.with(
|
game::init(&mut app);
|
||||||
tracing_subscriber::fmt::layer()
|
match app.run() {
|
||||||
.with_target(true)
|
AppExit::Success => {}
|
||||||
.with_thread_ids(true)
|
AppExit::Error(e) => {
|
||||||
.with_file(true)
|
log::error!("Error running new app: {e}");
|
||||||
.with_line_number(true),
|
|
||||||
)
|
|
||||||
.with(tracing_tracy::TracyLayer::default())
|
|
||||||
.with(
|
|
||||||
tracing_subscriber::EnvFilter::try_from_default_env()
|
|
||||||
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")),
|
|
||||||
)
|
|
||||||
.init();
|
|
||||||
|
|
||||||
let input_manager = InputManager::new(HashMap::from([
|
|
||||||
(
|
|
||||||
"move_forward".to_string(),
|
|
||||||
vec![
|
|
||||||
VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyW), AxisDirection::Normal),
|
|
||||||
VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyS), AxisDirection::Invert),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"move_right".to_string(),
|
|
||||||
vec![
|
|
||||||
VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyD), AxisDirection::Normal),
|
|
||||||
VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyA), AxisDirection::Invert),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"mouse_x".to_string(),
|
|
||||||
vec![VirtualBinding::MouseX(AxisDirection::Normal)],
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"mouse_y".to_string(),
|
|
||||||
vec![VirtualBinding::MouseY(AxisDirection::Normal)],
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"mouse_wheel".to_string(),
|
|
||||||
vec![VirtualBinding::MouseWheelY(AxisDirection::Normal)],
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"mouse_left".to_string(),
|
|
||||||
vec![VirtualBinding::MouseButton(
|
|
||||||
MouseButton::Left,
|
|
||||||
AxisDirection::Normal,
|
|
||||||
)],
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"mouse_right".to_string(),
|
|
||||||
vec![VirtualBinding::MouseButton(
|
|
||||||
MouseButton::Right,
|
|
||||||
AxisDirection::Normal,
|
|
||||||
)],
|
|
||||||
),
|
|
||||||
]));
|
|
||||||
|
|
||||||
let device_extensions = DeviceExtensions {
|
|
||||||
khr_swapchain: true,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let device_features = DeviceFeatures {
|
|
||||||
dynamic_rendering: true,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let vulkano_config = VulkanoConfig {
|
|
||||||
print_device_name: true,
|
|
||||||
device_extensions,
|
|
||||||
device_features,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let vulkano_context = VulkanoContext::new(vulkano_config);
|
|
||||||
|
|
||||||
let event_loop = EventLoop::with_user_event().build().unwrap();
|
|
||||||
event_loop.set_control_flow(ControlFlow::Poll);
|
|
||||||
let proxy = event_loop.create_proxy();
|
|
||||||
|
|
||||||
let mut app = core::app::App::new(vulkano_context, input_manager, proxy);
|
|
||||||
|
|
||||||
match event_loop.run_app(&mut app) {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(e) => {
|
|
||||||
tracing::error!("Error running old app: {e}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue