|
|
|
@ -2,6 +2,7 @@ use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEve
|
|
|
|
|
use winit::event_loop::{ControlFlow, EventLoop};
|
|
|
|
|
|
|
|
|
|
use ash::vk;
|
|
|
|
|
use ash::util::Align;
|
|
|
|
|
use image;
|
|
|
|
|
use image::GenericImageView;
|
|
|
|
|
|
|
|
|
@ -13,11 +14,13 @@ use crate::utility::fps_limiter::*;
|
|
|
|
|
use crate::utility::{debug, platforms};
|
|
|
|
|
use crate::utility::tools::*;
|
|
|
|
|
use crate::entities::*;
|
|
|
|
|
use crate::VulkanApp;
|
|
|
|
|
use crate::shaders::shaders;
|
|
|
|
|
|
|
|
|
|
use std::ffi::CString;
|
|
|
|
|
use std::ptr;
|
|
|
|
|
use std::mem::size_of;
|
|
|
|
|
use std::mem;
|
|
|
|
|
use mem::size_of;
|
|
|
|
|
use std::default;
|
|
|
|
|
use std::os::raw::{ c_void, c_char };
|
|
|
|
|
|
|
|
|
@ -27,76 +30,6 @@ const WINDOW_TITLE: &'static str = "Template";
|
|
|
|
|
|
|
|
|
|
const IS_PAINT_FPS_COUNTER: bool = true;
|
|
|
|
|
|
|
|
|
|
pub struct ProgramProc {
|
|
|
|
|
pub event_loop: EventLoop<()>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ProgramProc {
|
|
|
|
|
|
|
|
|
|
pub fn new() -> ProgramProc {
|
|
|
|
|
// init window stuff
|
|
|
|
|
let event_loop = EventLoop::new();
|
|
|
|
|
|
|
|
|
|
ProgramProc { event_loop }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn main_loop(self, mut vulkan_app: App) {
|
|
|
|
|
|
|
|
|
|
let mut tick_counter = utility::fps_limiter::FPSLimiter::new();
|
|
|
|
|
|
|
|
|
|
self.event_loop.run(move |event, _, control_flow| {
|
|
|
|
|
|
|
|
|
|
match event {
|
|
|
|
|
| Event::WindowEvent { event, .. } => {
|
|
|
|
|
match event {
|
|
|
|
|
| WindowEvent::CloseRequested => {
|
|
|
|
|
vulkan_app.wait_device_idle();
|
|
|
|
|
*control_flow = ControlFlow::Exit
|
|
|
|
|
},
|
|
|
|
|
| WindowEvent::KeyboardInput { input, .. } => {
|
|
|
|
|
match input {
|
|
|
|
|
| KeyboardInput { virtual_keycode, state, .. } => {
|
|
|
|
|
match (virtual_keycode, state) {
|
|
|
|
|
| (Some(VirtualKeyCode::Escape), ElementState::Pressed) => {
|
|
|
|
|
vulkan_app.wait_device_idle();
|
|
|
|
|
*control_flow = ControlFlow::Exit
|
|
|
|
|
},
|
|
|
|
|
| _ => {},
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
| WindowEvent::Resized(_new_size) => {
|
|
|
|
|
vulkan_app.wait_device_idle();
|
|
|
|
|
vulkan_app.is_framebuffer_resized = true;
|
|
|
|
|
},
|
|
|
|
|
| _ => {},
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
| Event::MainEventsCleared => {
|
|
|
|
|
&vulkan_app.window.request_redraw();
|
|
|
|
|
},
|
|
|
|
|
| Event::RedrawRequested(_window_id) => {
|
|
|
|
|
let delta_time = tick_counter.delta_time();
|
|
|
|
|
vulkan_app.draw_frame();
|
|
|
|
|
|
|
|
|
|
if IS_PAINT_FPS_COUNTER {
|
|
|
|
|
print!("FPS: {}\r", tick_counter.fps());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tick_counter.tick_frame();
|
|
|
|
|
},
|
|
|
|
|
| Event::LoopDestroyed => {
|
|
|
|
|
vulkan_app.wait_device_idle();
|
|
|
|
|
},
|
|
|
|
|
_ => (),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn create_instance(
|
|
|
|
|
entry: &ash::Entry,
|
|
|
|
|
window_title: &str,
|
|
|
|
@ -240,9 +173,8 @@ pub fn find_memory_type(
|
|
|
|
|
panic!("Failed to find suitable memory type!")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct App {
|
|
|
|
|
pub struct App<'a> {
|
|
|
|
|
pub window: winit::window::Window,
|
|
|
|
|
pub event_loop: winit::event_loop::EventLoop<()>,
|
|
|
|
|
|
|
|
|
|
// vulkan stuff
|
|
|
|
|
pub entry: ash::Entry,
|
|
|
|
@ -313,12 +245,15 @@ pub struct App {
|
|
|
|
|
pub last_time: f64,
|
|
|
|
|
|
|
|
|
|
pub physical_device_memory_properties: vk::PhysicalDeviceMemoryProperties,
|
|
|
|
|
|
|
|
|
|
pub query_pool: vk::QueryPool,
|
|
|
|
|
pub query_memory: vk::DeviceMemory,
|
|
|
|
|
pub query_mapped: &'a mut [u64],
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl App {
|
|
|
|
|
impl<'a> App<'a> {
|
|
|
|
|
|
|
|
|
|
pub fn new() -> Self {
|
|
|
|
|
let event_loop = winit::event_loop::EventLoop::new();
|
|
|
|
|
pub fn new(event_loop: &winit::event_loop::EventLoop<()>) -> Self {
|
|
|
|
|
let window = init_window(&event_loop, WINDOW_TITLE, WINDOW_WIDTH, WINDOW_HEIGHT);
|
|
|
|
|
|
|
|
|
|
let entry = unsafe { ash::Entry::load().unwrap() };
|
|
|
|
@ -393,14 +328,91 @@ impl App {
|
|
|
|
|
|
|
|
|
|
let command_buffers = Self::create_command_buffers(&device, command_pool);
|
|
|
|
|
|
|
|
|
|
let compute_command_buffers = Self::create_command_buffers(&device, command_pool);
|
|
|
|
|
let compute_command_buffers = Self::create_compute_command_buffers(&device, command_pool);
|
|
|
|
|
|
|
|
|
|
let sync_objects = SyncObjects::new(&device, MAX_FRAMES_IN_FLIGHT);
|
|
|
|
|
|
|
|
|
|
//setup logging of compute shader invocations
|
|
|
|
|
|
|
|
|
|
// Define the buffer to hold query results
|
|
|
|
|
let mut query_results_data: [u64; 1] = [0]; // Assuming we're querying one result
|
|
|
|
|
|
|
|
|
|
// Create a buffer to hold the query results
|
|
|
|
|
let buffer_info = vk::BufferCreateInfo {
|
|
|
|
|
s_type: vk::StructureType::BUFFER_CREATE_INFO,
|
|
|
|
|
size: 8,
|
|
|
|
|
usage: vk::BufferUsageFlags::TRANSFER_DST,
|
|
|
|
|
sharing_mode: vk::SharingMode::EXCLUSIVE,
|
|
|
|
|
..Default::default()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let query_result_buffer = unsafe {
|
|
|
|
|
device
|
|
|
|
|
.create_buffer(&buffer_info, None)
|
|
|
|
|
.expect("Failed to create query result buffer")
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Allocate memory for the buffer
|
|
|
|
|
let mem_requirements = unsafe { device.get_buffer_memory_requirements(query_result_buffer) };
|
|
|
|
|
let memory_type_index = find_memory_type(
|
|
|
|
|
mem_requirements.memory_type_bits,
|
|
|
|
|
vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT,
|
|
|
|
|
&physical_device_memory_properties,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let allocate_info = vk::MemoryAllocateInfo {
|
|
|
|
|
s_type: vk::StructureType::MEMORY_ALLOCATE_INFO,
|
|
|
|
|
allocation_size: mem_requirements.size,
|
|
|
|
|
memory_type_index,
|
|
|
|
|
..Default::default()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let query_memory = unsafe {
|
|
|
|
|
device
|
|
|
|
|
.allocate_memory(&allocate_info, None)
|
|
|
|
|
.expect("Failed to allocate query result memory")
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
// Bind the buffer with allocated memory
|
|
|
|
|
device
|
|
|
|
|
.bind_buffer_memory(query_result_buffer, query_memory, 0)
|
|
|
|
|
.expect("Failed to bind buffer memory")
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Now we can use this buffer to store query results
|
|
|
|
|
let query_mapped = unsafe {
|
|
|
|
|
let raw_ptr = device
|
|
|
|
|
.map_memory(
|
|
|
|
|
query_memory,
|
|
|
|
|
0,
|
|
|
|
|
std::mem::size_of_val(&query_results_data) as u64,
|
|
|
|
|
vk::MemoryMapFlags::empty(),
|
|
|
|
|
)
|
|
|
|
|
.expect("Failed to map memory");
|
|
|
|
|
std::slice::from_raw_parts_mut(raw_ptr as *mut u64, 1)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Create a query pool
|
|
|
|
|
let query_pool_info = vk::QueryPoolCreateInfo {
|
|
|
|
|
s_type: vk::StructureType::QUERY_POOL_CREATE_INFO,
|
|
|
|
|
query_type: vk::QueryType::PIPELINE_STATISTICS,
|
|
|
|
|
query_count: 1, // We're only querying one statistic
|
|
|
|
|
pipeline_statistics: vk::QueryPipelineStatisticFlags::COMPUTE_SHADER_INVOCATIONS,
|
|
|
|
|
..Default::default()
|
|
|
|
|
};
|
|
|
|
|
let query_pool = unsafe {
|
|
|
|
|
device
|
|
|
|
|
.create_query_pool(&query_pool_info, None)
|
|
|
|
|
.expect("Failed to create query pool")
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Self {
|
|
|
|
|
window,
|
|
|
|
|
event_loop,
|
|
|
|
|
|
|
|
|
|
entry,
|
|
|
|
|
instance,
|
|
|
|
@ -451,80 +463,15 @@ impl App {
|
|
|
|
|
last_time: 0.0,
|
|
|
|
|
|
|
|
|
|
physical_device_memory_properties,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*fn create_vertex_buffer(
|
|
|
|
|
instance: &ash::Instance,
|
|
|
|
|
device: &ash::Device,
|
|
|
|
|
physical_device: vk::PhysicalDevice,
|
|
|
|
|
) -> (vk::Buffer, vk::DeviceMemory) {
|
|
|
|
|
let vertex_buffer_create_info = vk::BufferCreateInfo {
|
|
|
|
|
s_type: vk::StructureType::BUFFER_CREATE_INFO,
|
|
|
|
|
p_next: ptr::null(),
|
|
|
|
|
flags: vk::BufferCreateFlags::empty(),
|
|
|
|
|
size: std::mem::size_of_val(&TRI_VERT_DATA) as u64,
|
|
|
|
|
usage: vk::BufferUsageFlags::VERTEX_BUFFER
|
|
|
|
|
| vk::BufferUsageFlags::STORAGE_BUFFER
|
|
|
|
|
| vk::BufferUsageFlags::TRANSFER_DST,
|
|
|
|
|
sharing_mode: vk::SharingMode::EXCLUSIVE,
|
|
|
|
|
queue_family_index_count: 0,
|
|
|
|
|
p_queue_family_indices: ptr::null(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let vertex_buffer = unsafe {
|
|
|
|
|
device
|
|
|
|
|
.create_buffer(&vertex_buffer_create_info, None)
|
|
|
|
|
.expect("Failed to create Vertex Buffer")
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let mem_requirements = unsafe { device.get_buffer_memory_requirements(vertex_buffer) };
|
|
|
|
|
let mem_properties =
|
|
|
|
|
unsafe { instance.get_physical_device_memory_properties(physical_device) };
|
|
|
|
|
let required_memory_flags: vk::MemoryPropertyFlags =
|
|
|
|
|
vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT;
|
|
|
|
|
let memory_type = App::find_memory_type(
|
|
|
|
|
mem_requirements.memory_type_bits,
|
|
|
|
|
required_memory_flags,
|
|
|
|
|
mem_properties,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let allocate_info = vk::MemoryAllocateInfo {
|
|
|
|
|
s_type: vk::StructureType::MEMORY_ALLOCATE_INFO,
|
|
|
|
|
p_next: ptr::null(),
|
|
|
|
|
allocation_size: mem_requirements.size,
|
|
|
|
|
memory_type_index: memory_type,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let vertex_buffer_memory = unsafe {
|
|
|
|
|
device
|
|
|
|
|
.allocate_memory(&allocate_info, None)
|
|
|
|
|
.expect("Failed to allocate vertex buffer memory!")
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
device
|
|
|
|
|
.bind_buffer_memory(vertex_buffer, vertex_buffer_memory, 0)
|
|
|
|
|
.expect("Failed to bind Buffer");
|
|
|
|
|
|
|
|
|
|
let data_ptr = device
|
|
|
|
|
.map_memory(
|
|
|
|
|
vertex_buffer_memory,
|
|
|
|
|
0,
|
|
|
|
|
vertex_buffer_create_info.size,
|
|
|
|
|
vk::MemoryMapFlags::empty(),
|
|
|
|
|
)
|
|
|
|
|
.expect("Failed to Map Memory") as *mut Vertex;
|
|
|
|
|
|
|
|
|
|
data_ptr.copy_from_nonoverlapping(TRI_VERT_DATA.as_ptr(), TRI_VERT_DATA.len());
|
|
|
|
|
query_pool,
|
|
|
|
|
query_memory,
|
|
|
|
|
query_mapped,
|
|
|
|
|
|
|
|
|
|
device.unmap_memory(vertex_buffer_memory);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
(vertex_buffer, vertex_buffer_memory)
|
|
|
|
|
}*/
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn find_memory_type(
|
|
|
|
|
type_filter: u32,
|
|
|
|
|
required_properties: vk::MemoryPropertyFlags,
|
|
|
|
@ -549,6 +496,7 @@ impl App {
|
|
|
|
|
for (shader, stage_i) in shaders() {
|
|
|
|
|
//check if graphics shader
|
|
|
|
|
if stage_i == vk::ShaderStageFlags::VERTEX || stage_i == vk::ShaderStageFlags::FRAGMENT {
|
|
|
|
|
println!("shader stage: {:?}", stage_i);
|
|
|
|
|
shader_modules.push(
|
|
|
|
|
vk::PipelineShaderStageCreateInfo {
|
|
|
|
|
s_type: vk::StructureType::PIPELINE_SHADER_STAGE_CREATE_INFO,
|
|
|
|
@ -812,25 +760,35 @@ impl App {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn record_compute_command_buffer(
|
|
|
|
|
&self,
|
|
|
|
|
device: &ash::Device,
|
|
|
|
|
&mut self,
|
|
|
|
|
command_buffer: vk::CommandBuffer
|
|
|
|
|
) {
|
|
|
|
|
let begin_info = vk::CommandBufferBeginInfo::default();
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
device
|
|
|
|
|
self.device
|
|
|
|
|
.begin_command_buffer(command_buffer, &begin_info)
|
|
|
|
|
.expect("failed to begin recording compute command buffer");
|
|
|
|
|
|
|
|
|
|
device
|
|
|
|
|
println!("resetting query pool for command buffer: {}", self.current_frame);
|
|
|
|
|
|
|
|
|
|
self.device
|
|
|
|
|
.cmd_reset_query_pool(
|
|
|
|
|
self.compute_command_buffers[self.current_frame],
|
|
|
|
|
self.query_pool,
|
|
|
|
|
0,
|
|
|
|
|
1,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
self.device.cmd_begin_query(self.compute_command_buffers[self.current_frame], self.query_pool, 0, vk::QueryControlFlags::empty());
|
|
|
|
|
self.device
|
|
|
|
|
.cmd_bind_pipeline(
|
|
|
|
|
command_buffer,
|
|
|
|
|
vk::PipelineBindPoint::COMPUTE,
|
|
|
|
|
self.compute_pipeline,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
device
|
|
|
|
|
self.device
|
|
|
|
|
.cmd_bind_descriptor_sets(
|
|
|
|
|
command_buffer,
|
|
|
|
|
vk::PipelineBindPoint::COMPUTE,
|
|
|
|
@ -840,169 +798,36 @@ impl App {
|
|
|
|
|
&[],
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
device
|
|
|
|
|
self.device
|
|
|
|
|
.cmd_dispatch(
|
|
|
|
|
command_buffer,
|
|
|
|
|
(PARTICLE_COUNT / 256) as u32,
|
|
|
|
|
1,
|
|
|
|
|
1,
|
|
|
|
|
);
|
|
|
|
|
device
|
|
|
|
|
.end_command_buffer(command_buffer)
|
|
|
|
|
.expect("failed ending compute command buffer");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn draw_frame(&mut self) {
|
|
|
|
|
//compute submission
|
|
|
|
|
let mut submit_infos;
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
self.device
|
|
|
|
|
.wait_for_fences(
|
|
|
|
|
&[self.sync_objects.compute_inflight_fences[self.current_frame]],
|
|
|
|
|
true,
|
|
|
|
|
u64::MAX,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.update_uniform_buffer(self.current_frame);
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
self.device
|
|
|
|
|
.reset_fences(&[self.sync_objects.compute_inflight_fences[self.current_frame]]);
|
|
|
|
|
|
|
|
|
|
self.device
|
|
|
|
|
.reset_command_buffer(self.compute_command_buffers[self.current_frame], vk::CommandBufferResetFlags::empty());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.record_compute_command_buffer(&self.device, self.compute_command_buffers[self.current_frame]);
|
|
|
|
|
|
|
|
|
|
submit_infos = [vk::SubmitInfo {
|
|
|
|
|
s_type: vk::StructureType::SUBMIT_INFO,
|
|
|
|
|
p_next: ptr::null(),
|
|
|
|
|
command_buffer_count: 1,
|
|
|
|
|
p_command_buffers: &self.compute_command_buffers[self.current_frame],
|
|
|
|
|
signal_semaphore_count: 1,
|
|
|
|
|
p_signal_semaphores: &self.sync_objects.compute_finished_semaphores[self.current_frame],
|
|
|
|
|
..Default::default()
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
self.device
|
|
|
|
|
.queue_submit(
|
|
|
|
|
self.compute_queue,
|
|
|
|
|
&submit_infos,
|
|
|
|
|
self.sync_objects.compute_inflight_fences[self.current_frame]
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//graphics submission
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
self.device
|
|
|
|
|
.wait_for_fences(
|
|
|
|
|
&[self.sync_objects.inflight_fences[self.current_frame]],
|
|
|
|
|
true,
|
|
|
|
|
u64::MAX,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let (image_index, _is_sub_optimal) = unsafe {
|
|
|
|
|
let result = self.swapchain_stuff.swapchain_loader.acquire_next_image(
|
|
|
|
|
self.swapchain_stuff.swapchain,
|
|
|
|
|
std::u64::MAX,
|
|
|
|
|
self.sync_objects.image_available_semaphores[self.current_frame],
|
|
|
|
|
vk::Fence::null(),
|
|
|
|
|
);
|
|
|
|
|
match result {
|
|
|
|
|
Ok(image_index) => image_index,
|
|
|
|
|
Err(vk_result) => match vk_result {
|
|
|
|
|
vk::Result::ERROR_OUT_OF_DATE_KHR => {
|
|
|
|
|
SwapChainStuff::recreate_swapchain(self);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
_ => panic!("Failed to acquire Swap Chain Image!"),
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
self.device
|
|
|
|
|
.reset_fences(&[self.sync_objects.inflight_fences[self.current_frame]]);
|
|
|
|
|
self.device
|
|
|
|
|
.reset_command_buffer(
|
|
|
|
|
self.command_buffers[self.current_frame],
|
|
|
|
|
vk::CommandBufferResetFlags::empty(),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.record_command_buffer(self.command_buffers[self.current_frame], image_index as usize);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let wait_semaphores = [
|
|
|
|
|
self.sync_objects.compute_finished_semaphores[self.current_frame],
|
|
|
|
|
self.sync_objects.image_available_semaphores[self.current_frame],
|
|
|
|
|
];
|
|
|
|
|
let wait_stages = [
|
|
|
|
|
vk::PipelineStageFlags::VERTEX_INPUT,
|
|
|
|
|
vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT,
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
submit_infos = [vk::SubmitInfo {
|
|
|
|
|
s_type: vk::StructureType::SUBMIT_INFO,
|
|
|
|
|
wait_semaphore_count: 2,
|
|
|
|
|
p_wait_semaphores: wait_semaphores.as_ptr(),
|
|
|
|
|
p_wait_dst_stage_mask: wait_stages.as_ptr(),
|
|
|
|
|
p_next: ptr::null(),
|
|
|
|
|
command_buffer_count: 1,
|
|
|
|
|
p_command_buffers: &self.command_buffers[self.current_frame],
|
|
|
|
|
signal_semaphore_count: 1,
|
|
|
|
|
p_signal_semaphores: &self.sync_objects.render_finished_semaphores[self.current_frame],
|
|
|
|
|
..Default::default()
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
self.device
|
|
|
|
|
.queue_submit(
|
|
|
|
|
self.graphics_queue,
|
|
|
|
|
&submit_infos,
|
|
|
|
|
self.sync_objects.inflight_fences[self.current_frame],
|
|
|
|
|
//log compute shader invocations
|
|
|
|
|
|
|
|
|
|
self.device.cmd_end_query(self.compute_command_buffers[self.current_frame], self.query_pool, 0);
|
|
|
|
|
|
|
|
|
|
/*let query_results = unsafe {
|
|
|
|
|
self.device.get_query_pool_results::<u64>(
|
|
|
|
|
self.query_pool,
|
|
|
|
|
0, // Starting query index
|
|
|
|
|
1, // Number of queries to retrieve (1 in this case)
|
|
|
|
|
self.query_mapped, // Results will be stored here
|
|
|
|
|
//0, // Query results stride (optional)
|
|
|
|
|
vk::QueryResultFlags::TYPE_64, // Flags specifying the type of query results
|
|
|
|
|
)
|
|
|
|
|
.expect("failed to submit draw command buffer");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let swapchains = [self.swapchain_stuff.swapchain];
|
|
|
|
|
.expect("Failed to retrieve query pool results"
|
|
|
|
|
)};
|
|
|
|
|
|
|
|
|
|
let present_info = vk::PresentInfoKHR {
|
|
|
|
|
s_type: vk::StructureType::PRESENT_INFO_KHR,
|
|
|
|
|
p_next: ptr::null(),
|
|
|
|
|
wait_semaphore_count: 1,
|
|
|
|
|
p_wait_semaphores: &self.sync_objects.render_finished_semaphores[self.current_frame],
|
|
|
|
|
swapchain_count: 1,
|
|
|
|
|
p_swapchains: swapchains.as_ptr(),
|
|
|
|
|
p_image_indices: &image_index,
|
|
|
|
|
p_results: ptr::null_mut(),
|
|
|
|
|
};
|
|
|
|
|
let result = unsafe {
|
|
|
|
|
self.swapchain_stuff.swapchain_loader
|
|
|
|
|
.queue_present(self.present_queue, &present_info)
|
|
|
|
|
};
|
|
|
|
|
println!("Compute shader invocations: {:?}", self.query_mapped[0]);*/
|
|
|
|
|
|
|
|
|
|
let is_resized = match result {
|
|
|
|
|
Ok(_) => self.is_framebuffer_resized,
|
|
|
|
|
Err(vk_result) => match vk_result {
|
|
|
|
|
vk::Result::ERROR_OUT_OF_DATE_KHR | vk::Result::SUBOPTIMAL_KHR => true,
|
|
|
|
|
_ => panic!("Failed to execute queue present."),
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
if is_resized {
|
|
|
|
|
self.is_framebuffer_resized = false;
|
|
|
|
|
SwapChainStuff::recreate_swapchain(self);
|
|
|
|
|
self.device
|
|
|
|
|
.end_command_buffer(command_buffer)
|
|
|
|
|
.expect("failed ending compute command buffer");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.current_frame = (self.current_frame + 1) % MAX_FRAMES_IN_FLIGHT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn create_shader_storage_buffers(
|
|
|
|
@ -1013,7 +838,13 @@ impl App {
|
|
|
|
|
) -> (Vec<vk::Buffer>, Vec<vk::DeviceMemory>) {
|
|
|
|
|
let mut particles = Particle::gen();
|
|
|
|
|
|
|
|
|
|
let buffer_size: u64 = std::mem::size_of::<Particle>() as u64 * PARTICLE_COUNT as u64;
|
|
|
|
|
let mut particles_slice_data = particles.as_mut_slice();
|
|
|
|
|
|
|
|
|
|
//let mut slice = Align::new();
|
|
|
|
|
|
|
|
|
|
let mut buffer_size: u64 = std::mem::size_of::<Particle>() as u64 * particles_slice_data.len() as u64;
|
|
|
|
|
|
|
|
|
|
println!("particles count: {}, particle size: {}, buffer size: {}", PARTICLE_COUNT as u64, std::mem::size_of::<Particle>() as u64, buffer_size as usize);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let (staging_buffer, staging_buffer_memory) = Self::create_buffer(
|
|
|
|
@ -1029,7 +860,9 @@ impl App {
|
|
|
|
|
let mut shader_storage_buffers_memory = vec![];
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
let data = device
|
|
|
|
|
println!("isize max is {}", isize::MAX);
|
|
|
|
|
println!("mapping device memory");
|
|
|
|
|
let mut data = device
|
|
|
|
|
.map_memory(
|
|
|
|
|
staging_buffer_memory,
|
|
|
|
|
0,
|
|
|
|
@ -1037,11 +870,21 @@ impl App {
|
|
|
|
|
vk::MemoryMapFlags::empty(),
|
|
|
|
|
)
|
|
|
|
|
.expect("failed to map shader storage buffer memory");
|
|
|
|
|
ptr::copy_nonoverlapping(
|
|
|
|
|
|
|
|
|
|
let mem_requirements = unsafe { device.get_buffer_memory_requirements(staging_buffer) };
|
|
|
|
|
|
|
|
|
|
println!("mem req: {}", mem_requirements.size);
|
|
|
|
|
let mut slice = Align::new(
|
|
|
|
|
data,
|
|
|
|
|
particles.as_mut_ptr() as *mut c_void,
|
|
|
|
|
buffer_size as usize,
|
|
|
|
|
mem::align_of::<Particle>() as u64,
|
|
|
|
|
mem_requirements.size,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
slice.copy_from_slice(&particles_slice_data);
|
|
|
|
|
|
|
|
|
|
println!("DATA INSIDE PARTICLES BUFFER: {}", ParticlesList::from_raw_ptr(data as *const Particle, particles.len()));
|
|
|
|
|
|
|
|
|
|
//println!("copying to device memory");
|
|
|
|
|
device
|
|
|
|
|
.unmap_memory(staging_buffer_memory);
|
|
|
|
|
|
|
|
|
@ -1052,7 +895,8 @@ impl App {
|
|
|
|
|
vk::BufferUsageFlags::STORAGE_BUFFER |
|
|
|
|
|
vk::BufferUsageFlags::VERTEX_BUFFER |
|
|
|
|
|
vk::BufferUsageFlags::TRANSFER_DST,
|
|
|
|
|
vk::MemoryPropertyFlags::DEVICE_LOCAL,
|
|
|
|
|
vk::MemoryPropertyFlags::DEVICE_LOCAL |
|
|
|
|
|
vk::MemoryPropertyFlags::HOST_VISIBLE,
|
|
|
|
|
physical_device_memory_properties,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
@ -1134,6 +978,9 @@ impl App {
|
|
|
|
|
device.end_command_buffer(alloced_command_buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
println!("src_buffer: {:?}", src_buffer);
|
|
|
|
|
println!("dst_buffer: {:?}", dst_buffer);
|
|
|
|
|
|
|
|
|
|
let submit_info = vk::SubmitInfo {
|
|
|
|
|
s_type: vk::StructureType::SUBMIT_INFO,
|
|
|
|
|
command_buffer_count: 1,
|
|
|
|
@ -1177,6 +1024,7 @@ impl App {
|
|
|
|
|
p_queue_family_indices: ptr::null(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
println!("actual buffer size: {}", size);
|
|
|
|
|
let buffer = unsafe {
|
|
|
|
|
device
|
|
|
|
|
.create_buffer(&buffer_create_info, None)
|
|
|
|
@ -1185,6 +1033,8 @@ impl App {
|
|
|
|
|
|
|
|
|
|
let mem_requirements = unsafe { device.get_buffer_memory_requirements(buffer) };
|
|
|
|
|
|
|
|
|
|
println!("memory allocation size: {}", mem_requirements.size);
|
|
|
|
|
|
|
|
|
|
let allocate_info = vk::MemoryAllocateInfo {
|
|
|
|
|
s_type: vk::StructureType::MEMORY_ALLOCATE_INFO,
|
|
|
|
|
p_next: ptr::null(),
|
|
|
|
@ -1278,6 +1128,7 @@ impl App {
|
|
|
|
|
|
|
|
|
|
let physical_device_features = vk::PhysicalDeviceFeatures {
|
|
|
|
|
sampler_anisotropy: vk::TRUE, // enable anisotropy device feature from Chapter-24.
|
|
|
|
|
pipeline_statistics_query: vk::TRUE,
|
|
|
|
|
..Default::default()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
@ -1349,7 +1200,7 @@ impl App {
|
|
|
|
|
}
|
|
|
|
|
let clear_color = [vk::ClearValue {
|
|
|
|
|
color: vk::ClearColorValue {
|
|
|
|
|
float32: [1.0, 1.0, 1.0, 1.0],
|
|
|
|
|
float32: [0.0, 0.0, 1.0, 1.0],
|
|
|
|
|
},
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
@ -1424,6 +1275,7 @@ impl App {
|
|
|
|
|
&[self.shader_storage_buffers[self.current_frame]],
|
|
|
|
|
offsets,
|
|
|
|
|
);
|
|
|
|
|
println!("binding to shader storage buffer index: {}", self.current_frame);
|
|
|
|
|
self.device
|
|
|
|
|
.cmd_draw(
|
|
|
|
|
command_buffer,
|
|
|
|
@ -1851,6 +1703,8 @@ impl App {
|
|
|
|
|
range: size_of::<UniformBufferObject>() as u64,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
println!("STORAGE BUFFERS INDEXES: {}, {}", (i as i32 - 1) as usize % MAX_FRAMES_IN_FLIGHT, i);
|
|
|
|
|
|
|
|
|
|
let storage_buffer_info_last_frame = vk::DescriptorBufferInfo {
|
|
|
|
|
buffer: shader_storage_buffers[(i as i32 - 1) as usize % MAX_FRAMES_IN_FLIGHT],
|
|
|
|
|
offset: 0,
|
|
|
|
@ -1978,6 +1832,27 @@ impl App {
|
|
|
|
|
|
|
|
|
|
command_buffers
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn create_compute_command_buffers(
|
|
|
|
|
device: &ash::Device,
|
|
|
|
|
command_pool: vk::CommandPool,
|
|
|
|
|
) -> Vec<vk::CommandBuffer> {
|
|
|
|
|
let command_buffer_allocate_info = vk::CommandBufferAllocateInfo {
|
|
|
|
|
s_type: vk::StructureType::COMMAND_BUFFER_ALLOCATE_INFO,
|
|
|
|
|
p_next: ptr::null(),
|
|
|
|
|
command_buffer_count: MAX_FRAMES_IN_FLIGHT as u32,
|
|
|
|
|
command_pool,
|
|
|
|
|
level: vk::CommandBufferLevel::PRIMARY,
|
|
|
|
|
};
|
|
|
|
|
let command_buffers = unsafe {
|
|
|
|
|
device
|
|
|
|
|
.allocate_command_buffers(&command_buffer_allocate_info)
|
|
|
|
|
.expect("Failed to allocate Command Buffers!")
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
command_buffers
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn wait_device_idle(&self) {
|
|
|
|
|
unsafe {
|
|
|
|
|
self.device
|
|
|
|
@ -1986,3 +1861,238 @@ impl App {
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl VulkanApp for App<'_> {
|
|
|
|
|
|
|
|
|
|
fn wait_device_idle(&self) {
|
|
|
|
|
unsafe {
|
|
|
|
|
self.device
|
|
|
|
|
.device_wait_idle()
|
|
|
|
|
.expect("Failed to wait device idle!")
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn resize_framebuffer(&mut self) {
|
|
|
|
|
self.is_framebuffer_resized = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn window_ref(&self) -> &winit::window::Window {
|
|
|
|
|
&self.window
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn cleanup_swapchain(&self) {
|
|
|
|
|
self.swapchain_stuff.cleanup_swapchain(&self.device)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn recreate_swapchain(&mut self) {
|
|
|
|
|
SwapChainStuff::recreate_swapchain(self);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn draw_frame(&mut self) {
|
|
|
|
|
let mut buffer_size: u64 = std::mem::size_of::<Particle>() as u64 * PARTICLE_COUNT as u64;
|
|
|
|
|
|
|
|
|
|
//compute submission
|
|
|
|
|
let mut submit_infos;
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
self.device
|
|
|
|
|
.wait_for_fences(
|
|
|
|
|
&[self.sync_objects.compute_inflight_fences[self.current_frame]],
|
|
|
|
|
true,
|
|
|
|
|
u64::MAX,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.update_uniform_buffer(self.current_frame);
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
self.device
|
|
|
|
|
.reset_fences(&[self.sync_objects.compute_inflight_fences[self.current_frame]]);
|
|
|
|
|
|
|
|
|
|
self.device
|
|
|
|
|
.reset_command_buffer(self.compute_command_buffers[self.current_frame], vk::CommandBufferResetFlags::empty());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//RESET query pool for compute shader statistics
|
|
|
|
|
/*unsafe {
|
|
|
|
|
self.device
|
|
|
|
|
.cmd_reset_query_pool(
|
|
|
|
|
self.compute_command_buffers[self.current_frame],
|
|
|
|
|
self.query_pool,
|
|
|
|
|
0,
|
|
|
|
|
1,
|
|
|
|
|
);
|
|
|
|
|
}*/
|
|
|
|
|
|
|
|
|
|
self.record_compute_command_buffer(self.compute_command_buffers[self.current_frame]);
|
|
|
|
|
self.update_uniform_buffer(self.current_frame);
|
|
|
|
|
|
|
|
|
|
submit_infos = [vk::SubmitInfo {
|
|
|
|
|
s_type: vk::StructureType::SUBMIT_INFO,
|
|
|
|
|
p_next: ptr::null(),
|
|
|
|
|
command_buffer_count: 1,
|
|
|
|
|
p_command_buffers: &self.compute_command_buffers[self.current_frame],
|
|
|
|
|
signal_semaphore_count: 1,
|
|
|
|
|
p_signal_semaphores: &self.sync_objects.compute_finished_semaphores[self.current_frame],
|
|
|
|
|
..Default::default()
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
self.device
|
|
|
|
|
.queue_submit(
|
|
|
|
|
self.compute_queue,
|
|
|
|
|
&submit_infos,
|
|
|
|
|
self.sync_objects.compute_inflight_fences[self.current_frame]
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*log compute shader invocations
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
self.device.cmd_end_query(self.compute_command_buffers[self.current_frame], query_pool, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let query_results = unsafe {
|
|
|
|
|
self.device.get_query_pool_results::<u32>(
|
|
|
|
|
query_pool,
|
|
|
|
|
0, // Starting query index
|
|
|
|
|
1, // Number of queries to retrieve (1 in this case)
|
|
|
|
|
data, // Results will be stored here
|
|
|
|
|
//0, // Query results stride (optional)
|
|
|
|
|
vk::QueryResultFlags::TYPE_64, // Flags specifying the type of query results
|
|
|
|
|
)
|
|
|
|
|
.expect("Failed to retrieve query pool results"
|
|
|
|
|
)};
|
|
|
|
|
|
|
|
|
|
println!("Compute shader invocations: {:?}", data[0]);*/
|
|
|
|
|
|
|
|
|
|
//graphics submission
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
self.device
|
|
|
|
|
.wait_for_fences(
|
|
|
|
|
&[self.sync_objects.inflight_fences[self.current_frame]],
|
|
|
|
|
true,
|
|
|
|
|
u64::MAX,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let (image_index, _is_sub_optimal) = unsafe {
|
|
|
|
|
let result = self.swapchain_stuff.swapchain_loader.acquire_next_image(
|
|
|
|
|
self.swapchain_stuff.swapchain,
|
|
|
|
|
std::u64::MAX,
|
|
|
|
|
self.sync_objects.image_available_semaphores[self.current_frame],
|
|
|
|
|
vk::Fence::null(),
|
|
|
|
|
);
|
|
|
|
|
match result {
|
|
|
|
|
Ok(image_index) => image_index,
|
|
|
|
|
Err(vk_result) => match vk_result {
|
|
|
|
|
vk::Result::ERROR_OUT_OF_DATE_KHR => {
|
|
|
|
|
SwapChainStuff::recreate_swapchain(self);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
_ => panic!("Failed to acquire Swap Chain Image!"),
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
self.device
|
|
|
|
|
.reset_fences(&[self.sync_objects.inflight_fences[self.current_frame]]);
|
|
|
|
|
self.device
|
|
|
|
|
.reset_command_buffer(
|
|
|
|
|
self.command_buffers[self.current_frame],
|
|
|
|
|
vk::CommandBufferResetFlags::empty(),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.record_command_buffer(self.command_buffers[self.current_frame], image_index as usize);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let wait_semaphores = [
|
|
|
|
|
self.sync_objects.compute_finished_semaphores[self.current_frame],
|
|
|
|
|
self.sync_objects.image_available_semaphores[self.current_frame],
|
|
|
|
|
];
|
|
|
|
|
let wait_stages = [
|
|
|
|
|
vk::PipelineStageFlags::VERTEX_INPUT,
|
|
|
|
|
vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT,
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
submit_infos = [vk::SubmitInfo {
|
|
|
|
|
s_type: vk::StructureType::SUBMIT_INFO,
|
|
|
|
|
wait_semaphore_count: 2,
|
|
|
|
|
p_wait_semaphores: wait_semaphores.as_ptr(),
|
|
|
|
|
p_wait_dst_stage_mask: wait_stages.as_ptr(),
|
|
|
|
|
p_next: ptr::null(),
|
|
|
|
|
command_buffer_count: 1,
|
|
|
|
|
p_command_buffers: &self.command_buffers[self.current_frame],
|
|
|
|
|
signal_semaphore_count: 1,
|
|
|
|
|
p_signal_semaphores: &self.sync_objects.render_finished_semaphores[self.current_frame],
|
|
|
|
|
..Default::default()
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
self.device
|
|
|
|
|
.queue_submit(
|
|
|
|
|
self.graphics_queue,
|
|
|
|
|
&submit_infos,
|
|
|
|
|
self.sync_objects.inflight_fences[self.current_frame],
|
|
|
|
|
)
|
|
|
|
|
.expect("failed to submit draw command buffer");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let swapchains = [self.swapchain_stuff.swapchain];
|
|
|
|
|
|
|
|
|
|
let present_info = vk::PresentInfoKHR {
|
|
|
|
|
s_type: vk::StructureType::PRESENT_INFO_KHR,
|
|
|
|
|
p_next: ptr::null(),
|
|
|
|
|
wait_semaphore_count: 1,
|
|
|
|
|
p_wait_semaphores: &self.sync_objects.render_finished_semaphores[self.current_frame],
|
|
|
|
|
swapchain_count: 1,
|
|
|
|
|
p_swapchains: swapchains.as_ptr(),
|
|
|
|
|
p_image_indices: &image_index,
|
|
|
|
|
p_results: ptr::null_mut(),
|
|
|
|
|
};
|
|
|
|
|
let result = unsafe {
|
|
|
|
|
self.swapchain_stuff.swapchain_loader
|
|
|
|
|
.queue_present(self.present_queue, &present_info)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let is_resized = match result {
|
|
|
|
|
Ok(_) => self.is_framebuffer_resized,
|
|
|
|
|
Err(vk_result) => match vk_result {
|
|
|
|
|
vk::Result::ERROR_OUT_OF_DATE_KHR | vk::Result::SUBOPTIMAL_KHR => true,
|
|
|
|
|
_ => panic!("Failed to execute queue present."),
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
if is_resized {
|
|
|
|
|
self.is_framebuffer_resized = false;
|
|
|
|
|
SwapChainStuff::recreate_swapchain(self);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//println!("resized {}", self.is_framebuffer_resized);
|
|
|
|
|
|
|
|
|
|
//log compute shader invocations
|
|
|
|
|
|
|
|
|
|
/*unsafe {
|
|
|
|
|
self.device.cmd_end_query(self.compute_command_buffers[self.current_frame], self.query_pool, 0);
|
|
|
|
|
}*/
|
|
|
|
|
|
|
|
|
|
let query_results = unsafe {
|
|
|
|
|
self.device.get_query_pool_results::<u64>(
|
|
|
|
|
self.query_pool,
|
|
|
|
|
0, // Starting query index
|
|
|
|
|
1, // Number of queries to retrieve (1 in this case)
|
|
|
|
|
self.query_mapped, // Results will be stored here
|
|
|
|
|
//0, // Query results stride (optional)
|
|
|
|
|
vk::QueryResultFlags::TYPE_64, // Flags specifying the type of query results
|
|
|
|
|
)};
|
|
|
|
|
|
|
|
|
|
println!("Compute shader invocations: {:?}", self.query_mapped[0]);
|
|
|
|
|
|
|
|
|
|
self.current_frame = (self.current_frame + 1) % MAX_FRAMES_IN_FLIGHT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|