cross-platform 👀
This commit is contained in:
parent
8a1c5237d5
commit
12b20be584
7 changed files with 193 additions and 110 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -2,3 +2,4 @@
|
||||||
sponza/
|
sponza/
|
||||||
.direnv/
|
.direnv/
|
||||||
shader-cache/
|
shader-cache/
|
||||||
|
log-debug.log
|
||||||
|
|
|
||||||
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -412,6 +412,7 @@ name = "engine"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ash",
|
"ash",
|
||||||
|
"ash-window",
|
||||||
"egui",
|
"egui",
|
||||||
"gfx_hal",
|
"gfx_hal",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ members = [
|
||||||
ash = { version = "0.38" }
|
ash = { version = "0.38" }
|
||||||
ash-window = "0.13.0"
|
ash-window = "0.13.0"
|
||||||
color-eyre = "0.6.3"
|
color-eyre = "0.6.3"
|
||||||
winit = { version = "0.30.7", features = ["rwh_06"] }
|
winit = { version = "0.30.9", features = ["rwh_06"] }
|
||||||
raw-window-handle = "0.6"
|
raw-window-handle = "0.6"
|
||||||
gpu-allocator = { version = "0.27.0", features = ["vulkan"] }
|
gpu-allocator = { version = "0.27.0", features = ["vulkan"] }
|
||||||
glam = { version = "0.22", default-features = false, features = [
|
glam = { version = "0.22", default-features = false, features = [
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ edition = "2021"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
egui.workspace = true
|
egui.workspace = true
|
||||||
ash.workspace = true
|
ash.workspace = true
|
||||||
|
ash-window.workspace = true
|
||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
tracing-subscriber.workspace = true
|
tracing-subscriber.workspace = true
|
||||||
winit.workspace = true
|
winit.workspace = true
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
use std::{
|
use std::{
|
||||||
error::Error,
|
error::Error,
|
||||||
ffi::{CStr, CString},
|
ffi::{c_char, CStr, CString},
|
||||||
fs::OpenOptions,
|
fs::OpenOptions,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
time::Instant,
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
use ash::vk;
|
use ash::vk;
|
||||||
|
|
@ -11,23 +11,20 @@ use gfx_hal::{
|
||||||
device::Device, error::GfxHalError, instance::Instance, instance::InstanceConfig,
|
device::Device, error::GfxHalError, instance::Instance, instance::InstanceConfig,
|
||||||
physical_device::PhysicalDevice, queue::Queue, surface::Surface,
|
physical_device::PhysicalDevice, queue::Queue, surface::Surface,
|
||||||
};
|
};
|
||||||
use raw_window_handle::HasDisplayHandle;
|
use raw_window_handle::{HasDisplayHandle, HasRawDisplayHandle};
|
||||||
use renderer::{Renderer, RendererError};
|
use renderer::{Renderer, RendererError};
|
||||||
use resource_manager::{ResourceManager, ResourceManagerError};
|
use resource_manager::{ResourceManager, ResourceManagerError};
|
||||||
use tracing::{debug, error, info, warn};
|
use tracing::{debug, error, info, warn};
|
||||||
use tracing_subscriber::{filter, layer::SubscriberExt, util::SubscriberInitExt, Layer};
|
use tracing_subscriber::{filter, layer::SubscriberExt, util::SubscriberInitExt, Layer};
|
||||||
use winit::{
|
use winit::{
|
||||||
|
application::ApplicationHandler,
|
||||||
event::{Event, WindowEvent},
|
event::{Event, WindowEvent},
|
||||||
event_loop::{ActiveEventLoop, EventLoop},
|
event_loop::{ActiveEventLoop, EventLoop},
|
||||||
window::{Window, WindowAttributes},
|
window::{Window, WindowAttributes},
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Configuration ---
|
// --- Configuration ---
|
||||||
const WINDOW_TITLE: &str = "Rust Vulkan Egui Engine";
|
const APP_NAME: &str = "BeginDisregard";
|
||||||
const INITIAL_WIDTH: u32 = 1280;
|
const ENGINE_NAME: &str = "Engine";
|
||||||
const INITIAL_HEIGHT: u32 = 720;
|
|
||||||
const APP_NAME: &str = "My App";
|
|
||||||
const ENGINE_NAME: &str = "My Engine";
|
|
||||||
|
|
||||||
// --- Error Handling ---
|
// --- Error Handling ---
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
|
@ -50,14 +47,12 @@ enum AppError {
|
||||||
MissingExtension(String),
|
MissingExtension(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Main Application Structure ---
|
|
||||||
struct Application {
|
struct Application {
|
||||||
// Core Vulkan Objects (managed by gfx_hal)
|
|
||||||
_instance: Arc<Instance>, // Keep instance alive
|
_instance: Arc<Instance>, // Keep instance alive
|
||||||
_physical_device: PhysicalDevice, // Keep info, though Device holds handle
|
_physical_device: PhysicalDevice, // Keep info, though Device holds handle
|
||||||
device: Arc<Device>,
|
device: Arc<Device>,
|
||||||
graphics_queue: Arc<Queue>,
|
_graphics_queue: Arc<Queue>,
|
||||||
surface: Arc<Surface>,
|
_surface: Arc<Surface>,
|
||||||
|
|
||||||
// Resource Management
|
// Resource Management
|
||||||
resource_manager: Arc<ResourceManager>,
|
resource_manager: Arc<ResourceManager>,
|
||||||
|
|
@ -68,9 +63,39 @@ struct Application {
|
||||||
// Windowing
|
// Windowing
|
||||||
window: Arc<Window>, // Use Arc for potential multi-threading later
|
window: Arc<Window>, // Use Arc for potential multi-threading later
|
||||||
|
|
||||||
// State
|
frame_count: u32,
|
||||||
|
last_fps_update_time: Instant,
|
||||||
last_frame_time: Instant,
|
last_frame_time: Instant,
|
||||||
ui_show_demo: bool,
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct ApplicationWrapper {
|
||||||
|
app: Option<Application>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApplicationHandler for ApplicationWrapper {
|
||||||
|
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
|
let window = Arc::new(
|
||||||
|
event_loop
|
||||||
|
.create_window(
|
||||||
|
Window::default_attributes()
|
||||||
|
.with_title(format!("{} - {}", ENGINE_NAME, APP_NAME,)),
|
||||||
|
)
|
||||||
|
.expect("Windows to be able to be created"),
|
||||||
|
);
|
||||||
|
self.app = Some(Application::new(window).expect("Unable to create the Application."));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn window_event(
|
||||||
|
&mut self,
|
||||||
|
event_loop: &ActiveEventLoop,
|
||||||
|
_window_id: winit::window::WindowId,
|
||||||
|
event: WindowEvent,
|
||||||
|
) {
|
||||||
|
if let Some(app) = &mut self.app {
|
||||||
|
app.handle_event(&event, event_loop);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Application {
|
impl Application {
|
||||||
|
|
@ -78,20 +103,25 @@ impl Application {
|
||||||
info!("Initializing Application...");
|
info!("Initializing Application...");
|
||||||
|
|
||||||
// --- 1. gfx_hal Setup ---
|
// --- 1. gfx_hal Setup ---
|
||||||
let instance_extensions = [
|
let instance_extensions =
|
||||||
// Add extensions required by the platform (e.g., from winit)
|
ash_window::enumerate_required_extensions(window.display_handle().unwrap().into())
|
||||||
// ash::extensions::ext::DebugUtils::name(), // If using validation
|
.unwrap();
|
||||||
ash::khr::surface::NAME,
|
|
||||||
// Platform specific (example for Xlib/Wayland)
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
ash::khr::xlib_surface::NAME,
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
ash::khr::wayland_surface::NAME,
|
|
||||||
// Add other platform extensions as needed (Win32, Metal, etc.)
|
|
||||||
];
|
|
||||||
let instance_extensions_c: Vec<CString> = instance_extensions
|
let instance_extensions_c: Vec<CString> = instance_extensions
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&s| CString::new(s.to_bytes()).unwrap())
|
.map(|&ptr| {
|
||||||
|
// Safety: We are trusting that the pointers returned by
|
||||||
|
// ash_window::enumerate_required_extensions are valid, non-null,
|
||||||
|
// null-terminated C strings. This is a standard assumption when
|
||||||
|
// working with C APIs via FFI.
|
||||||
|
unsafe {
|
||||||
|
// 1. Create a borrowed CStr reference from the raw pointer.
|
||||||
|
let c_str = CStr::from_ptr(ptr as *const c_char); // Cast is optional but common
|
||||||
|
|
||||||
|
// 2. Convert the borrowed CStr into an owned CString.
|
||||||
|
c_str.to_owned()
|
||||||
|
}
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let instance_config = InstanceConfig {
|
let instance_config = InstanceConfig {
|
||||||
|
|
@ -137,7 +167,6 @@ impl Application {
|
||||||
&pd,
|
&pd,
|
||||||
&surface,
|
&surface,
|
||||||
&required_device_extensions_cstr,
|
&required_device_extensions_cstr,
|
||||||
&required_dynamic_rendering_features,
|
|
||||||
) {
|
) {
|
||||||
Ok(indices) => Some((pd, indices)),
|
Ok(indices) => Some((pd, indices)),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
@ -238,66 +267,67 @@ impl Application {
|
||||||
_instance: instance,
|
_instance: instance,
|
||||||
_physical_device: physical_device,
|
_physical_device: physical_device,
|
||||||
device,
|
device,
|
||||||
graphics_queue,
|
_graphics_queue: graphics_queue,
|
||||||
surface,
|
_surface: surface,
|
||||||
resource_manager,
|
resource_manager,
|
||||||
renderer,
|
renderer,
|
||||||
window,
|
window,
|
||||||
|
frame_count: 0,
|
||||||
|
last_fps_update_time: Instant::now(),
|
||||||
last_frame_time: Instant::now(),
|
last_frame_time: Instant::now(),
|
||||||
ui_show_demo: true,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_event(&mut self, event: &Event<()>, active_event_loop: &ActiveEventLoop) {
|
fn handle_event(&mut self, event: &WindowEvent, active_event_loop: &ActiveEventLoop) {
|
||||||
match event {
|
match event {
|
||||||
Event::WindowEvent { event, window_id } if *window_id == self.window.id() => {
|
WindowEvent::CloseRequested => {
|
||||||
match event {
|
info!("Close requested. Exiting...");
|
||||||
WindowEvent::CloseRequested => {
|
active_event_loop.exit();
|
||||||
info!("Close requested. Exiting...");
|
|
||||||
active_event_loop.exit();
|
|
||||||
}
|
|
||||||
WindowEvent::Resized(physical_size) => {
|
|
||||||
info!(
|
|
||||||
"Window resized to: {}x{}",
|
|
||||||
physical_size.width, physical_size.height
|
|
||||||
);
|
|
||||||
// Important: Resize renderer *before* the next frame
|
|
||||||
self.renderer
|
|
||||||
.resize(physical_size.width, physical_size.height);
|
|
||||||
// Egui also needs the new screen descriptor info, though
|
|
||||||
// egui_winit_state might handle this internally via on_window_event.
|
|
||||||
// Explicitly setting it might be safer depending on version.
|
|
||||||
// self.egui_winit_state.set_max_size_points(...) // If needed
|
|
||||||
}
|
|
||||||
WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
|
|
||||||
info!("Scale factor changed: {}", scale_factor);
|
|
||||||
// May also need to resize renderer if size depends on scale factor
|
|
||||||
let new_inner_size = self.window.inner_size();
|
|
||||||
self.renderer
|
|
||||||
.resize(new_inner_size.width, new_inner_size.height);
|
|
||||||
}
|
|
||||||
// Handle other inputs if not consumed by egui
|
|
||||||
WindowEvent::KeyboardInput { .. }
|
|
||||||
| WindowEvent::CursorMoved { .. }
|
|
||||||
| WindowEvent::MouseInput { .. } => {}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Event::MainEventsCleared => { // Use AboutToWait for newer winit
|
WindowEvent::Resized(physical_size) => {
|
||||||
// // Application update code.
|
info!(
|
||||||
// self.window.request_redraw();
|
"Window resized to: {}x{}",
|
||||||
// }
|
physical_size.width, physical_size.height
|
||||||
Event::AboutToWait => {
|
);
|
||||||
// Application update code and redraw request.
|
// Important: Resize renderer *before* the next frame
|
||||||
// This is the main place to prepare and trigger rendering.
|
self.renderer
|
||||||
|
.resize(physical_size.width, physical_size.height);
|
||||||
|
}
|
||||||
|
WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
|
||||||
|
info!("Scale factor changed: {}", scale_factor);
|
||||||
|
// May also need to resize renderer if size depends on scale factor
|
||||||
|
let new_inner_size = self.window.inner_size();
|
||||||
|
self.renderer
|
||||||
|
.resize(new_inner_size.width, new_inner_size.height);
|
||||||
|
}
|
||||||
|
// Handle other inputs if not consumed by egui
|
||||||
|
WindowEvent::KeyboardInput { .. }
|
||||||
|
| WindowEvent::CursorMoved { .. }
|
||||||
|
| WindowEvent::MouseInput { .. } => {}
|
||||||
|
|
||||||
|
WindowEvent::RedrawRequested => {
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
let _delta_time = now.duration_since(self.last_frame_time);
|
let _delta_time = now.duration_since(self.last_frame_time);
|
||||||
self.last_frame_time = now;
|
self.last_frame_time = now;
|
||||||
|
|
||||||
|
let elapsed_sice_last_update = now.duration_since(self.last_fps_update_time);
|
||||||
|
self.frame_count += 1;
|
||||||
|
|
||||||
|
if elapsed_sice_last_update >= Duration::from_secs(1) {
|
||||||
|
let fps = self.frame_count as f64 / elapsed_sice_last_update.as_secs_f64();
|
||||||
|
|
||||||
|
let new_title = format!("{} - {} - {:.0} FPS", ENGINE_NAME, APP_NAME, fps);
|
||||||
|
self.window.set_title(&new_title);
|
||||||
|
|
||||||
|
self.frame_count = 0;
|
||||||
|
self.last_fps_update_time = now;
|
||||||
|
}
|
||||||
|
|
||||||
// --- Render Frame ---
|
// --- Render Frame ---
|
||||||
match self.renderer.render_frame() {
|
match self.renderer.render_frame() {
|
||||||
Ok(_) => {}
|
Ok(_) => {
|
||||||
|
self.window.request_redraw();
|
||||||
|
}
|
||||||
Err(RendererError::SwapchainSuboptimal) => {
|
Err(RendererError::SwapchainSuboptimal) => {
|
||||||
// Swapchain is suboptimal, recreate it next frame by triggering resize
|
// Swapchain is suboptimal, recreate it next frame by triggering resize
|
||||||
warn!("Swapchain suboptimal, forcing resize.");
|
warn!("Swapchain suboptimal, forcing resize.");
|
||||||
|
|
@ -310,14 +340,8 @@ impl Application {
|
||||||
active_event_loop.exit();
|
active_event_loop.exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Event::LoopExiting => {
|
self.window.as_ref().request_redraw();
|
||||||
info!("Event loop exiting. Cleaning up...");
|
|
||||||
// Wait for GPU to finish before dropping resources
|
|
||||||
if let Err(e) = self.device.wait_idle() {
|
|
||||||
error!("Error waiting for device idle on exit: {}", e);
|
|
||||||
}
|
|
||||||
info!("GPU idle. Cleanup complete.");
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
@ -331,7 +355,6 @@ fn find_suitable_device_and_queues(
|
||||||
physical_device: &PhysicalDevice,
|
physical_device: &PhysicalDevice,
|
||||||
surface: &Surface,
|
surface: &Surface,
|
||||||
required_extensions: &[&CStr],
|
required_extensions: &[&CStr],
|
||||||
required_dynamic_rendering_features: &vk::PhysicalDeviceDynamicRenderingFeaturesKHR,
|
|
||||||
) -> Result<gfx_hal::physical_device::QueueFamilyIndices, Box<dyn Error>> {
|
) -> Result<gfx_hal::physical_device::QueueFamilyIndices, Box<dyn Error>> {
|
||||||
// 1. Check Extension Support
|
// 1. Check Extension Support
|
||||||
let supported_extensions = unsafe {
|
let supported_extensions = unsafe {
|
||||||
|
|
@ -425,7 +448,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||||
.with_ansi(true)
|
.with_ansi(true)
|
||||||
.with_file(false)
|
.with_file(false)
|
||||||
.with_line_number(false)
|
.with_line_number(false)
|
||||||
.without_time();
|
.with_filter(filter::LevelFilter::DEBUG);
|
||||||
|
|
||||||
let log_file = OpenOptions::new()
|
let log_file = OpenOptions::new()
|
||||||
.append(true)
|
.append(true)
|
||||||
|
|
@ -433,9 +456,10 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||||
.open("log-debug.log")?;
|
.open("log-debug.log")?;
|
||||||
|
|
||||||
let json_layer = tracing_subscriber::fmt::layer()
|
let json_layer = tracing_subscriber::fmt::layer()
|
||||||
.json()
|
.with_ansi(false)
|
||||||
|
.without_time()
|
||||||
.with_writer(log_file)
|
.with_writer(log_file)
|
||||||
.with_filter(filter::LevelFilter::TRACE);
|
.with_filter(filter::LevelFilter::DEBUG);
|
||||||
|
|
||||||
tracing_subscriber::registry()
|
tracing_subscriber::registry()
|
||||||
.with(fmt_layer)
|
.with(fmt_layer)
|
||||||
|
|
@ -444,19 +468,11 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
|
||||||
// --- Winit Setup ---
|
// --- Winit Setup ---
|
||||||
let event_loop = EventLoop::new()?;
|
let event_loop = EventLoop::new()?;
|
||||||
let window = Arc::new(event_loop.create_window(WindowAttributes::default())?);
|
|
||||||
|
|
||||||
info!("Window created.");
|
|
||||||
|
|
||||||
// --- Application Setup ---
|
|
||||||
let mut app = Application::new(window.clone())?;
|
|
||||||
|
|
||||||
// --- Event Loop ---
|
// --- Event Loop ---
|
||||||
info!("Starting event loop...");
|
info!("Starting event loop...");
|
||||||
event_loop.run(move |event, elwt| {
|
let mut app = ApplicationWrapper::default();
|
||||||
// elwt is EventLoopWindowTarget, not needed directly here often
|
event_loop.run_app(&mut app)?;
|
||||||
app.handle_event(&event, elwt);
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use parking_lot::Mutex;
|
||||||
use resource_manager::{ImageHandle, ResourceManager, ResourceManagerError};
|
use resource_manager::{ImageHandle, ResourceManager, ResourceManagerError};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tracing::{debug, error, info, warn};
|
use tracing::{debug, error, info, warn};
|
||||||
// Assuming winit is used by the app
|
// Assuming winit is used by the app
|
||||||
|
|
||||||
// Re-export ash for convenience if needed elsewhere
|
// Re-export ash for convenience if needed elsewhere
|
||||||
pub use ash;
|
pub use ash;
|
||||||
|
|
@ -179,16 +179,17 @@ impl Renderer {
|
||||||
frame_data.in_flight_fence.wait(None)?; // Wait indefinitely
|
frame_data.in_flight_fence.wait(None)?; // Wait indefinitely
|
||||||
|
|
||||||
// --- Acquire Swapchain Image ---
|
// --- Acquire Swapchain Image ---
|
||||||
|
let swapchain_ref = self
|
||||||
|
.swapchain
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(RendererError::SwapchainAcquisitionFailed)?;
|
||||||
let (image_index, suboptimal) = unsafe {
|
let (image_index, suboptimal) = unsafe {
|
||||||
// Need unsafe block for acquire_next_image
|
// Need unsafe block for acquire_next_image
|
||||||
self.swapchain
|
swapchain_ref.acquire_next_image(
|
||||||
.as_ref()
|
u64::MAX, // Timeout
|
||||||
.ok_or(RendererError::SwapchainAcquisitionFailed)? // Should exist
|
Some(&frame_data.image_available_semaphore),
|
||||||
.acquire_next_image(
|
None, // Don't need a fence here
|
||||||
u64::MAX, // Timeout
|
)?
|
||||||
Some(&frame_data.image_available_semaphore),
|
|
||||||
None, // Don't need a fence here
|
|
||||||
)?
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if suboptimal {
|
if suboptimal {
|
||||||
|
|
@ -221,10 +222,40 @@ impl Renderer {
|
||||||
.begin_command_buffer(command_buffer, &cmd_begin_info)?;
|
.begin_command_buffer(command_buffer, &cmd_begin_info)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let current_swapchain_image = swapchain_ref.images()[image_index as usize];
|
||||||
|
|
||||||
|
let initial_layout_transition_barrier = vk::ImageMemoryBarrier::default()
|
||||||
|
.src_access_mask(vk::AccessFlags::empty()) // No need to wait for writes from previous frame/present
|
||||||
|
.dst_access_mask(vk::AccessFlags::COLOR_ATTACHMENT_WRITE) // Will be written as attachment
|
||||||
|
.old_layout(vk::ImageLayout::UNDEFINED) // Assume undefined or present_src; UNDEFINED is safer
|
||||||
|
.new_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL) // Layout needed for rendering
|
||||||
|
.src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
|
||||||
|
.dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
|
||||||
|
.image(current_swapchain_image)
|
||||||
|
.subresource_range(vk::ImageSubresourceRange {
|
||||||
|
aspect_mask: vk::ImageAspectFlags::COLOR,
|
||||||
|
base_mip_level: 0,
|
||||||
|
level_count: 1,
|
||||||
|
base_array_layer: 0,
|
||||||
|
layer_count: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
self.device.raw().cmd_pipeline_barrier(
|
||||||
|
command_buffer,
|
||||||
|
vk::PipelineStageFlags::TOP_OF_PIPE, // Source stage (nothing before this)
|
||||||
|
vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT, // Destination stage (where write happens)
|
||||||
|
vk::DependencyFlags::empty(),
|
||||||
|
&[], // No memory barriers
|
||||||
|
&[], // No buffer memory barriers
|
||||||
|
&[initial_layout_transition_barrier], // The image barrier
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// --- Dynamic Rendering Setup ---
|
// --- Dynamic Rendering Setup ---
|
||||||
let color_attachment = vk::RenderingAttachmentInfo::default()
|
let color_attachment = vk::RenderingAttachmentInfo::default()
|
||||||
.image_view(self.swapchain_image_views[image_index as usize])
|
.image_view(self.swapchain_image_views[image_index as usize])
|
||||||
.image_layout(vk::ImageLayout::ATTACHMENT_OPTIMAL)
|
.image_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
|
||||||
.load_op(vk::AttachmentLoadOp::CLEAR)
|
.load_op(vk::AttachmentLoadOp::CLEAR)
|
||||||
.store_op(vk::AttachmentStoreOp::STORE)
|
.store_op(vk::AttachmentStoreOp::STORE)
|
||||||
.clear_value(vk::ClearValue {
|
.clear_value(vk::ClearValue {
|
||||||
|
|
@ -303,6 +334,36 @@ impl Renderer {
|
||||||
self.device.raw().cmd_end_rendering(command_buffer);
|
self.device.raw().cmd_end_rendering(command_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let current_swapchain_image = swapchain_ref.images()[image_index as usize];
|
||||||
|
|
||||||
|
let layout_transition_barrier = vk::ImageMemoryBarrier::default()
|
||||||
|
.src_access_mask(vk::AccessFlags::COLOR_ATTACHMENT_WRITE)
|
||||||
|
.dst_access_mask(vk::AccessFlags::empty())
|
||||||
|
.old_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
|
||||||
|
.new_layout(vk::ImageLayout::PRESENT_SRC_KHR)
|
||||||
|
.src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
|
||||||
|
.dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
|
||||||
|
.image(current_swapchain_image)
|
||||||
|
.subresource_range(vk::ImageSubresourceRange {
|
||||||
|
aspect_mask: vk::ImageAspectFlags::COLOR,
|
||||||
|
base_mip_level: 0,
|
||||||
|
level_count: 1,
|
||||||
|
base_array_layer: 0,
|
||||||
|
layer_count: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
self.device.raw().cmd_pipeline_barrier(
|
||||||
|
command_buffer,
|
||||||
|
vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT,
|
||||||
|
vk::PipelineStageFlags::BOTTOM_OF_PIPE,
|
||||||
|
vk::DependencyFlags::empty(),
|
||||||
|
&[],
|
||||||
|
&[],
|
||||||
|
&[layout_transition_barrier],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// --- End Command Buffer ---
|
// --- End Command Buffer ---
|
||||||
unsafe {
|
unsafe {
|
||||||
// Need unsafe for Vulkan commands
|
// Need unsafe for Vulkan commands
|
||||||
|
|
@ -347,9 +408,7 @@ impl Renderer {
|
||||||
|
|
||||||
let suboptimal_present = unsafe {
|
let suboptimal_present = unsafe {
|
||||||
// Need unsafe for queue_present
|
// Need unsafe for queue_present
|
||||||
self.swapchain
|
swapchain_ref
|
||||||
.as_ref()
|
|
||||||
.unwrap() // Safe unwrap
|
|
||||||
.loader()
|
.loader()
|
||||||
.queue_present(self.graphics_queue.handle(), &present_info)
|
.queue_present(self.graphics_queue.handle(), &present_info)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
|
|
@ -536,7 +595,11 @@ impl Renderer {
|
||||||
.samples(vk::SampleCountFlags::TYPE_1)
|
.samples(vk::SampleCountFlags::TYPE_1)
|
||||||
.sharing_mode(vk::SharingMode::EXCLUSIVE);
|
.sharing_mode(vk::SharingMode::EXCLUSIVE);
|
||||||
|
|
||||||
let handle = resource_manager.create_image(&image_create_info, MemoryLocation::GpuOnly)?;
|
let handle = resource_manager.create_image(
|
||||||
|
&image_create_info,
|
||||||
|
MemoryLocation::GpuOnly,
|
||||||
|
vk::ImageAspectFlags::DEPTH,
|
||||||
|
)?;
|
||||||
|
|
||||||
// Get the vk::Image handle to create the view
|
// Get the vk::Image handle to create the view
|
||||||
let image_info = resource_manager.get_image_info(handle)?;
|
let image_info = resource_manager.get_image_info(handle)?;
|
||||||
|
|
|
||||||
|
|
@ -427,6 +427,7 @@ impl ResourceManager {
|
||||||
&self,
|
&self,
|
||||||
create_info: &vk::ImageCreateInfo, // User provides image details
|
create_info: &vk::ImageCreateInfo, // User provides image details
|
||||||
location: MemoryLocation,
|
location: MemoryLocation,
|
||||||
|
aspect_flags: vk::ImageAspectFlags,
|
||||||
) -> Result<ImageHandle> {
|
) -> Result<ImageHandle> {
|
||||||
trace!(
|
trace!(
|
||||||
"Creating image: format={:?}, extent={:?}, usage={:?}, location={:?}",
|
"Creating image: format={:?}, extent={:?}, usage={:?}, location={:?}",
|
||||||
|
|
@ -465,7 +466,7 @@ impl ResourceManager {
|
||||||
.view_type(vk::ImageViewType::TYPE_2D) // Assuming 2D, adjust based on create_info
|
.view_type(vk::ImageViewType::TYPE_2D) // Assuming 2D, adjust based on create_info
|
||||||
.format(create_info.format)
|
.format(create_info.format)
|
||||||
.subresource_range(vk::ImageSubresourceRange {
|
.subresource_range(vk::ImageSubresourceRange {
|
||||||
aspect_mask: vk::ImageAspectFlags::COLOR, // Assuming color, adjust based on usage
|
aspect_mask: aspect_flags,
|
||||||
base_mip_level: 0,
|
base_mip_level: 0,
|
||||||
level_count: create_info.mip_levels,
|
level_count: create_info.mip_levels,
|
||||||
base_array_layer: 0,
|
base_array_layer: 0,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue