From ebf6814651917544ef86138c74d83bdbcd5ed95a Mon Sep 17 00:00:00 2001 From: Albin Chaboissier Date: Thu, 9 Oct 2025 17:56:51 +0200 Subject: [PATCH] UDP reception --- Cargo.lock | 15 ++ Cargo.toml | 2 +- src/main.rs | 378 +++++++++++++++++++----------------- vulcain.h | 544 ---------------------------------------------------- 4 files changed, 222 insertions(+), 717 deletions(-) delete mode 100644 vulcain.h diff --git a/Cargo.lock b/Cargo.lock index dc14f3d..2eee39c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3136,6 +3136,16 @@ dependencies = [ "serde", ] +[[package]] +name = "socket2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "spirv" version = "0.3.0+sdk-1.3.268.0" @@ -3325,12 +3335,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", + "bytes", "io-uring", "libc", "mio", + "parking_lot", "pin-project-lite", + "signal-hook-registry", "slab", + "socket2", "tokio-macros", + "windows-sys 0.59.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e6cb8ff..2218f6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,4 @@ egui_plot = "0.33.0" hound = "3.5.1" plotters = "0.3.7" rand = "0.9.2" -tokio = { version = "1.47.1", features = ["macros", "sync", "time"] } +tokio = { version = "1.47.1", features = ["full", "macros", "net", "sync", "time"] } diff --git a/src/main.rs b/src/main.rs index f396ed2..e918e32 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,19 +5,27 @@ mod complex; pub mod fft; mod filtering; mod iq; +mod math; mod nco; +mod ted; mod units; mod windows; -mod ted; -mod math; - +use rand::{rand_core::le, seq::index::sample}; use std::{collections::VecDeque, time::Duration}; -use tokio::{join, select, time::timeout}; +use tokio::{join, net::UdpSocket, select, time::timeout}; +use crate::{ + bfsk::BFSKMod, + complex::Complex32, + filtering::{dc_block::DCBlocker, fir::FIRFilter}, + iq::IQSampler, + nco::Nco, + ted::elg::ELGate, + units::frequency::hz_to_rad_per_sample, +}; use eframe::{egui, glow::SAMPLE_MASK_VALUE}; use tokio::sync::mpsc::{Receiver, Sender, channel}; -use crate::{bfsk::BFSKMod, complex::Complex32, filtering::{dc_block::DCBlocker, fir::FIRFilter}, iq::IQSampler, nco::Nco, ted::elg::ELGate, units::frequency::hz_to_rad_per_sample}; const BAUD_RATE: u32 = 1000; const SAMPLE_RATE: u32 = 48000; @@ -26,112 +34,120 @@ const SAMPLE_RATE: u32 = 48000; const CENTER_FREQ: f32 = 1700.; const DEVIATION: f32 = 500.; -pub trait SampleSender -{ +pub trait SampleSender { fn open_link(&mut self); fn send_samples(&mut self, samples: &[f32]); fn close_link(&mut self); } -struct Transceiver -{ - data_receiver: Receiver>, - data_sender: Sender>, - samples_sender: Sender, -} +struct Transceiver {} -impl Transceiver -{ - pub async fn start(sample_sender: T) -> Self - { - let (transmitter_tx, transmitter_rx) = channel::>(4096); - let (acknowledged_tx, acknowledged_rx) = channel::<()>(32); - let (ack_tx, ack_rx) = channel::<()>(32); - let (samples_tx, samples_rx) = channel::(4096); - - let (receiver_tx, receiver_rx) = channel::>(4096); - join!( - Self::transmitter(acknowledged_rx, transmitter_rx, ack_rx, sample_sender), - Self::receiver(acknowledged_tx, samples_rx, receiver_tx, ack_tx) - ); - - Self - { - data_receiver: receiver_rx, - data_sender: transmitter_tx, - samples_sender: samples_tx - } - } - - async fn transmitter(mut acknowledged: Receiver<()>, mut data_receiver: Receiver>, mut ack_receiver: Receiver<()>, mut samples_sender: T) - { - let mut send_queue: VecDeque = VecDeque::new(); - - loop - { - if !send_queue.is_empty() - { - let to_send = send_queue.pop_front().unwrap(); - - // Create modulation - let bytes = to_send.bytes(); - let mut bit_stream = bytes.iter().flat_map(|x| byte_to_bits(*x)); - let mut modulator = BFSKMod::new((SAMPLE_RATE as f32 / BAUD_RATE as f32).round() as u32, - hz_to_rad_per_sample(DEVIATION, SAMPLE_RATE as f32), - &mut bit_stream); - - let mut up_lo = Nco::new(hz_to_rad_per_sample(CENTER_FREQ, SAMPLE_RATE as f32)); - - let mut sample_buffer = vec![]; - for (m, up) in modulator.zip(up_lo) +impl Transceiver { + pub async fn start( + mut sample_stream: Receiver, + mut tx_stream: Receiver>, + mut rx_stream: Sender>, + sample_sender: &mut T, + ) { + let mut resend: Option> = None; + loop { + select! { + _ = Self::squelch_detector(&mut sample_stream) => { - let sample = m * up; - sample_buffer.push(sample.re); // Project IQ - } - - samples_sender.open_link(); - samples_sender.send_samples(&sample_buffer); - samples_sender.close_link(); - - if let Frame::Data(_) = to_send - { - // Wait for ack - while !acknowledged.is_empty() + println!("Squelch UP"); + select! { - let _ = acknowledged.blocking_recv(); + x = Self::receive(&mut sample_stream) => + { + match x + { + Err(()) => {continue;}, + Ok(Frame::Ack) => + { + resend = None; + } + Ok(Frame::Data(data)) => + { + rx_stream.send(data).await.unwrap(); + Self::transmit(Frame::Ack, sample_sender).await; + } + } + }, + _ = tokio::time::sleep(Duration::from_secs(2)) => {continue;}, //TODO: 65 + //sec + //timeout } - - let ack_timout = timeout(Duration::from_secs(2), acknowledged.recv()).await; - if let Ok(Some(())) = ack_timout + }, // End squelch + data_opt = async + { + tokio::time::sleep(Duration::from_secs(2)).await; + if let Some(resend_data) = resend.clone() { - // ACK Received : Ok + Some(resend_data) } else { - // Try again - send_queue.push_front(to_send); + tx_stream.recv().await } } - } - else - { - let new = select! + => { - Some(x) = data_receiver.recv() => Frame::Data(x), - Some(()) = ack_receiver.recv() => Frame::Ack, - }; - - match new - { - Frame::Ack => send_queue.push_front(Frame::Ack), // Highest importance - Frame::Data(x) => send_queue.push_back(Frame::Data(x)) + if let Some(data) = data_opt + { + Self::transmit(Frame::Data(data.clone()), sample_sender).await; + resend = Some(data); + } } } } } - async fn receiver(acknowledged: Sender<()>, mut samples: Receiver, data_sender: Sender>, ack_sender: Sender<()>) - { + async fn squelch_detector(sample_stream: &mut Receiver) { + let length = 500; + let level = 0.01; + let mut iq_sampler = IQSampler::new(hz_to_rad_per_sample(CENTER_FREQ, SAMPLE_RATE as f32)); + let mut squelch_sum = 0.; + let mut i = 0; + while let Some(smpl) = sample_stream.recv().await { + println!("sdkf"); + let iq = iq_sampler.sample(smpl); + squelch_sum += iq.mag() / length as f32; + i += 1; + + if i >= length { + if squelch_sum >= level { + return; + } + } else { + i = 0; + squelch_sum = 0.; + } + } + } + + async fn transmit(frame: Frame, samples_sender: &mut T) { + let bytes = frame.bytes(); + let mut bit_stream = bytes.iter().flat_map(|x| byte_to_bits(*x)); + let modulator = BFSKMod::new( + (SAMPLE_RATE as f32 / BAUD_RATE as f32).round() as u32, + hz_to_rad_per_sample(DEVIATION, SAMPLE_RATE as f32), + &mut bit_stream, + ); + + let up_lo = Nco::new(hz_to_rad_per_sample(CENTER_FREQ, SAMPLE_RATE as f32)); + + let mut sample_buffer = vec![]; + for (m, up) in modulator.zip(up_lo) { + let sample = m * up; + sample_buffer.push(sample.re); // Project IQ + } + + samples_sender.open_link(); + samples_sender.send_samples(&sample_buffer); + samples_sender.close_link(); + } + + async fn receive(sample_stream: &mut Receiver) -> Result { let mut iq_sampler = IQSampler::new(hz_to_rad_per_sample(CENTER_FREQ, SAMPLE_RATE as f32)); let samples_per_symbol = (SAMPLE_RATE as f32) / (BAUD_RATE as f32); @@ -139,12 +155,19 @@ impl Transceiver let correllator_length = samples_per_symbol as usize; let mut pos_nco = Nco::new(hz_to_rad_per_sample(DEVIATION, SAMPLE_RATE as f32)); let mut neg_nco = Nco::new(hz_to_rad_per_sample(-DEVIATION, SAMPLE_RATE as f32)); - let pos_ir = (0..correllator_length).map(|_| {pos_nco.step(); pos_nco.cexp()}); - let neg_ir = (0..correllator_length).map(|_| {neg_nco.step(); neg_nco.cexp()}); + let pos_ir = (0..correllator_length).map(|_| { + pos_nco.step(); + pos_nco.cexp() + }); + let neg_ir = (0..correllator_length).map(|_| { + neg_nco.step(); + neg_nco.cexp() + }); let mut pos_correllator = FIRFilter::new(&pos_ir.collect::>()); let mut neg_correllator = FIRFilter::new(&neg_ir.collect::>()); - - let mut matched_lowpass = FIRFilter::new(&vec![Complex32::new(1., 0.); samples_per_symbol as usize]); + + let mut matched_lowpass = + FIRFilter::new(&vec![Complex32::new(1., 0.); samples_per_symbol as usize]); let mut dc_block = DCBlocker::new(0.995); let loop_i = 0.1; @@ -157,117 +180,100 @@ impl Transceiver let mut last_byte = 0x00u8; let mut frame_constructor = FrameConstructor::new(); let mut bit_count: Option = None; - while let Some(sample) = samples.recv().await - { - let iq = iq_sampler.sample(sample); - let matched = dc_block.next_real(matched_lowpass.next_real(pos_correllator.next(iq).mag() - neg_correllator.next(iq).mag())); - if let Some(bit_sample) = elg.next(matched) - { - last_byte <<= 1; + while let Some(sample) = sample_stream.recv().await { + let iq = iq_sampler.sample(sample); + let matched = + dc_block + .next_real(matched_lowpass.next_real( + pos_correllator.next(iq).mag() - neg_correllator.next(iq).mag(), + )); + if let Some(bit_sample) = elg.next(matched) { + last_byte <<= 1; last_byte |= (bit_sample > 0.) as u8; bit_count = bit_count.map(|x| x + 1); - if last_byte == 0xD8 // Potential frame starts + if last_byte == 0xD8 + // Potential frame starts { last_byte = 0; frame_constructor = FrameConstructor::new(); bit_count = Some(0); } - if let Some(8) = bit_count - { + if let Some(8) = bit_count { let frame_opt = frame_constructor.add_byte(last_byte); - if let Ok(None) = frame_opt - { - bit_count = Some(0); + bit_count = Some(0); + if let Ok(Some(Frame::Ack)) = frame_opt { + return Ok(Frame::Ack); } - if let Ok(Some(Frame::Ack)) = frame_opt - { - bit_count = None; - acknowledged.send(()).await.unwrap(); // Send acknowledgement to transmitter + if let Ok(Some(Frame::Data(ref frame_data))) = frame_opt { + return Ok(Frame::Data(frame_data.to_vec())); } - if let Ok(Some(Frame::Data(ref frame_data))) = frame_opt - { - bit_count = None; - data_sender.send(frame_data.to_vec()).await.unwrap(); - ack_sender.send(()).await.unwrap(); - } - - if let Err(()) = frame_opt - { - bit_count = None; // Erroneous frame, ignore + if let Err(()) = frame_opt { + // Erroneous frame + return Err(()); } } } } + return Err(()); } } -enum Frame -{ +enum Frame { Data(Vec), - Ack + Ack, } type FrameConstructionError = (); -pub struct FrameConstructor -{ +pub struct FrameConstructor { frame: Vec, frame_countdown: Option, checksum: u8, } -impl FrameConstructor -{ - pub fn new() -> Self - { - Self - { +impl FrameConstructor { + pub fn new() -> Self { + Self { frame: Vec::new(), frame_countdown: None, checksum: 0u8, } } - pub fn add_byte(&mut self, byte: u8) -> Result, FrameConstructionError> - { - if self.frame.is_empty() && byte != 0xC4 && byte != 0x4C - { - return Err(()); + pub fn add_byte(&mut self, byte: u8) -> Result, FrameConstructionError> { + if self.frame.is_empty() && byte != 0xC4 && byte != 0x4C { + return Err(()); } - if self.frame.is_empty() && byte == 0xC4 - { + if self.frame.is_empty() && byte == 0xC4 { return Ok(Some(Frame::Ack)); } - if self.frame.is_empty() && byte == 0x4C - { + if self.frame.is_empty() && byte == 0x4C { return Ok(None); } self.frame.push(byte); // Retrieve length - if self.frame.len() == 1 - { + if self.frame.len() == 1 { self.frame_countdown = Some(self.frame[0] as u16); return Ok(None); } - if self.frame.len() == 2 - { + if self.frame.len() == 2 { *self.frame_countdown.as_mut().unwrap() |= (self.frame[1] as u16) << 8; return Ok(None); } - if self.frame_countdown.unwrap() == 0 - { + if self.frame_countdown.unwrap() == 0 { // All data has been received - if self.checksum == byte - { - return Ok(Some(Frame::Data(self.frame.iter().skip(2).copied().collect()))); + if self.checksum == byte { + return Ok(Some(Frame::Data( + self.frame.iter().skip(2).copied().collect(), + ))); } return Err(()); @@ -281,12 +287,10 @@ impl FrameConstructor } } -impl Frame -{ - pub fn bytes(&self) -> Vec - { +impl Frame { + pub fn bytes(&self) -> Vec { let mut output_bytes = vec![]; - + // Initial training sequence output_bytes.append(&mut vec![0b01010101; 64]); @@ -294,13 +298,11 @@ impl Frame output_bytes.push(0xD8); // Command - match self - { - Frame::Data(x) => - { + match self { + Frame::Data(x) => { let mut checksum = 0u8; x.iter().for_each(|x| checksum ^= x); - + assert!(x.len() < 65536, "Data size over MTU"); let len_u16 = x.len() as u16; output_bytes.push(0x4C); // DATA FRAME @@ -311,8 +313,7 @@ impl Frame output_bytes.push(checksum); } - Frame::Ack => - { + Frame::Ack => { output_bytes.push(0xC4); // ACK FRAME } } @@ -323,7 +324,8 @@ impl Frame } } -fn main() { +#[tokio::main] +async fn main() { let native_options = eframe::NativeOptions::default(); let _ = eframe::run_native( "Egui", @@ -332,26 +334,58 @@ fn main() { ); } - //#[derive(Default)] -struct EguiApp { +struct EguiApp {} + +struct DummySampleSender(); + +impl SampleSender for DummySampleSender { + fn open_link(&mut self) {} + fn send_samples(&mut self, samples: &[f32]) {} + fn close_link(&mut self) {} } impl EguiApp { - fn new( - _cc: &eframe::CreationContext<'_>, - ) -> Self { + fn new(_cc: &eframe::CreationContext<'_>) -> Self { + let (sample_tx, sample_rx) = channel::(1024); - Self { + let (transmit_tx, transmit_rx) = channel::>(1024); + let (receive_tx, receive_rx) = channel::>(1024); - } + tokio::spawn(async move { + Transceiver::start(sample_rx, transmit_rx, receive_tx, &mut DummySampleSender()).await; + }); + + tokio::spawn(async move { + let sock = UdpSocket::bind("0.0.0.0:8080").await.unwrap(); + let mut buf = [0u8; 1024]; + let mut sample = 0i16; + let mut byte_index = 0; + + loop { + let len = sock.recv(&mut buf).await.unwrap(); + for x in buf.iter().take(len) { + sample |= (*x as i16) << (byte_index * 8); + byte_index += 1; + if byte_index >= 2 { + sample_tx + .send(sample as f32 / i16::MAX as f32) + .await + .unwrap(); + byte_index = 0; + sample = 0i16; + } + } + } + }); + + Self {} } } impl eframe::App for EguiApp { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { - egui::CentralPanel::default().show(ctx, |_ui| { - }); + egui::CentralPanel::default().show(ctx, |_ui| {}); } } diff --git a/vulcain.h b/vulcain.h deleted file mode 100644 index 08905af..0000000 --- a/vulcain.h +++ /dev/null @@ -1,544 +0,0 @@ -#ifndef __VULCAIN_H__ -#define __VULCAIN_H__ - -#include "vc_windowing.h" -#include "handles/vc_handles.h" -#include -#include -#include - -// ## TODO: Make those header private -#include -#include "descriptors/vc_ds_alloc.h" -#include "descriptors/vc_set_layout_cache.h" -// ## - -#include "femtolog.h" - -#define vc_trace(fmt, ...) \ - fl_log(TRACE, __FILE__, __LINE__, fmt, ## __VA_ARGS__); - -#define vc_debug(fmt, ...) \ - fl_log(DEBUG, __FILE__, __LINE__, fmt, ## __VA_ARGS__); - -#define vc_info(fmt, ...) \ - fl_log(INFO, __FILE__, __LINE__, fmt, ## __VA_ARGS__); - -#define vc_warn(fmt, ...) \ - fl_log(WARN, __FILE__, __LINE__, fmt, ## __VA_ARGS__); - -#define vc_error(fmt, ...) \ - fl_log(ERROR, __FILE__, __LINE__, fmt, ## __VA_ARGS__); - -#define vc_fatal(fmt, ...) \ - fl_log(FATAL, __FILE__, __LINE__, fmt, ## __VA_ARGS__); - -// Represents various features which need to be checked before use. -typedef struct -{ - b8 dynamic_rendering; -} vc_ctx_supported_features; - -// Welcome to vulcain -typedef struct -{ - vc_handles_manager handles_manager; - - bool windowing_enabled; // This means that the application runs in some sort of a window, so swapchains can be created. - bool debugging_enabled; // This is true if some validation layers are requested. - - vc_windowing_system windowing_system; // In the case a windowing system is being used. - - VkInstance vk_instance; - VkDebugUtilsMessengerEXT debugging_messenger; // Only used if debugging_enabled. - - VkPhysicalDevice current_physical_device; - VkDevice current_device; - - VmaAllocator main_allocator; // See if it would be a good idea to allow multiple allocators ... - - // TODO: Make those two invisible to the outside world - vc_descriptor_set_allocator ds_allocator; - vc_set_layout_cache set_layout_cache; - - vc_ctx_supported_features supported_features; - - // Optional features - void *imgui_ctx; -} vc_ctx; - -typedef struct -{ - uint32_t format_count; - VkFormat *formats; -} vc_format_set; - -typedef struct -{ - VkFormatFeatureFlags required_linear_tiling_features; - VkFormatFeatureFlags required_optimal_tiling_features; - VkFormatFeatureFlags required_buffer_features; -} vc_format_query; - -// ## VC_CTX ## - -bool vc_ctx_create(vc_ctx *ctx, - VkApplicationInfo app_info, - vc_windowing_system *windowing_system, - bool enable_debugging, - uint32_t layer_count, - const char **layer_names, - uint32_t extension_count, - const char **extension_names); - -void vc_ctx_destroy(vc_ctx *ctx); - -void vc_queue_wait_idle(vc_ctx *ctx, vc_queue queue); -void vc_device_wait_idle(vc_ctx *ctx); - -// ## FORMAT UTILS ## - -VkFormat vc_format_query_format(vc_ctx *ctx, vc_format_query query, vc_format_set candidates); -b8 vc_format_query_index(vc_ctx *ctx, vc_format_query query, vc_format_set candidates, u32 *index); - -// ## SWAPCHAIN ## - -/* - * @brief Describes the swapchain parameters, after a swapchain has been created - */ -typedef struct -{ - VkExtent2D swapchain_extent; - VkFormat swapchain_image_format; - vc_swapchain swapchain; - - uint32_t swapchain_image_count; - vc_image *images; - vc_image_view *image_views; - -} vc_swapchain_created_info; - -typedef void (*vc_swapchain_callback_func)(vc_ctx *ctx, void *udata, vc_swapchain_created_info); - -typedef u32 vc_swpchn_img_id; - -vc_swapchain vc_swapchain_create(vc_ctx *ctx, - vc_windowing_system win_sys, - VkImageUsageFlags image_usage, - vc_format_query query, - vc_swapchain_callback_func create_clbk, - vc_swapchain_callback_func destroy_clbk, - void *clbk_udata); - -void vc_swapchain_present_image(vc_ctx *ctx, vc_swapchain swapchain, vc_queue presentation_queue, vc_semaphore wait_semaphore, vc_swpchn_img_id image_id); -vc_swpchn_img_id vc_swapchain_acquire_image(vc_ctx *ctx, vc_swapchain swapchain, vc_semaphore *signal_semaphore); -vc_image vc_swapchain_get_image(vc_ctx *ctx, vc_swapchain swapchain, vc_swpchn_img_id index); -void vc_handle_destroy(vc_ctx *ctx, vc_handle hndl); -void vc_swapchain_get_info(vc_ctx *ctx, vc_swapchain swapchain, vc_swapchain_created_info *info_out); -void vc_swapchain_present_images(vc_ctx *ctx, u32 swapchain_count, vc_swapchain *swapchains, vc_swpchn_img_id *image_ids, vc_queue presentation_queue, u32 wait_semaphore_count, vc_semaphore *wait_semaphores); - -// ## COMMAND BUFFERS/POOLS ## - -/** - * @brief Creates a command pool - * - * @param ctx The vulcain context - * @param parent_queue A queue of the queue family to create the command pool with - * @param flags The flags with which to create te command pool - * @return A handle to a command pool - */ -vc_command_pool vc_command_pool_create(vc_ctx *ctx, vc_queue parent_queue, VkCommandPoolCreateFlags flags); - -/** - * @brief Allocates a command buffer - * - * @param ctx The vulcain context - * @param level The level of the command buffer - * @param pool The pool in which to allocate the command buffer - * @return A handle to a command buffer - */ -vc_command_buffer vc_command_buffer_allocate(vc_ctx *ctx, VkCommandBufferLevel level, vc_command_pool pool); - -// ## SYNCHRONISATOIN OBJECTS ## - -vc_semaphore vc_semaphore_create(vc_ctx *ctx); - -// ## IMAGES ## - -typedef struct -{ - VmaMemoryUsage usage; - VkMemoryPropertyFlags mem_props; - VmaAllocationCreateFlags flags; -} vc_memory_create_info; - -typedef struct -{ - uint8_t image_dimension; - VkFormat image_format; - uint32_t width; - uint32_t height; - uint32_t depth; - - uint32_t mip_level_count; - uint32_t array_layer_count; - - VkSampleCountFlagBits sample_count; - VkImageTiling tiling; - VkImageUsageFlags usage; - - b8 sharing_exclusive; - vc_queue *queues; - uint32_t queue_count; - - VkImageLayout initial_layout; - vc_memory_create_info memory; -} vc_image_create_info; - -vc_image vc_image_allocate(vc_ctx *ctx, vc_image_create_info create_info); -vc_image_view vc_image_view_create(vc_ctx *ctx, vc_image image, VkImageViewType type, VkComponentMapping component_map, VkImageSubresourceRange range); - -// Useful utils -#define VC_COMP_MAP_ID \ - (VkComponentMapping) \ - { \ - .a = VK_COMPONENT_SWIZZLE_A, \ - .r = VK_COMPONENT_SWIZZLE_R, \ - .g = VK_COMPONENT_SWIZZLE_G, \ - .b = VK_COMPONENT_SWIZZLE_B, \ - } - -#define VC_IMG_SUBRES_COLOR_1 \ - (VkImageSubresourceRange) \ - { \ - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, \ - .layerCount = 1, \ - .levelCount = 1, \ - .baseMipLevel = 0, \ - .baseArrayLayer = 0, \ - } - -// ## BUFFERS ## - -/** - * @brief Allocates a buffer - * - * @param ctx A vulcain context - * @param size The size of the buffer to allocate - * @param flags The flags with which to create the buffer - * @param usage The usage of the buffer - * @param mem The memory information about the allocation - * @return A handle to the buffer - */ -vc_buffer vc_buffer_allocate(vc_ctx *ctx, u64 size, VkBufferCreateFlags flags, VkBufferUsageFlags usage, vc_memory_create_info mem); - -// ## DESCRIPTORS ## - -// Set layouts - -typedef struct -{ - VkDescriptorSetLayoutBinding *bindings; // darray -} vc_descriptor_set_layout_builder; - -/** - * @brief Adds a single descriptor binding to descriptor set layout - * - * @param builder The builder (may be 0 initialized) - * @param binding The binding index - * @param type The descriptor type - * @param stages The stages - */ -void vc_descriptor_set_layout_builder_add_binding(vc_descriptor_set_layout_builder *builder, u32 binding, VkDescriptorType type, VkShaderStageFlags stages); -/** - * @brief Adds some descriptors to a descriptor set layout - * - * @param builder The builder (may be 0 initialized) - * @param binding The binding index - * @param descriptor_count The number of descriptors - * @param type The descriptor type - * @param stages The stages - */ -void vc_descriptor_set_layout_builder_add_bindings(vc_descriptor_set_layout_builder *builder, u32 binding, u32 descriptor_count, VkDescriptorType type, VkShaderStageFlags stages); - - -/** - * @brief Builds a descriptor set - * - * @param ctx A vulcain context - * @param builder The (non non-inited) builder - * @param flags The flags to create the set layout with - * @return A handle to the set layout - */ -vc_descriptor_set_layout vc_descriptor_set_layout_builder_build(vc_ctx *ctx, vc_descriptor_set_layout_builder *builder, VkDescriptorSetLayoutCreateFlags flags); - -// Descriptor sets - -/** - * @brief Allocates a descriptor in the internal pool system - * - * @param ctx The vulcain context - * @param layout The set layout with which to create the descriptor - * @return A handle to the allocated descriptor set - */ -vc_descriptor_set vc_descriptor_set_allocate(vc_ctx *ctx, vc_descriptor_set_layout layout); - -/** - * @brief Representes a writer, which helps writing into descriptor sets - */ -typedef struct -{ - // darrays - VkWriteDescriptorSet *writes; - - VkDescriptorImageInfo *img_infos; - VkDescriptorBufferInfo *buf_infos; -} vc_descriptor_set_writer; - -/** - * @brief Writes a buffer type descriptor into the descriptor set - * - * @param ctx A vulcain context - * @param writer The writer - * @param binding The destination binding - * @param array_elt The destination array element - * @param buffer The buffer handle - * @param offset The offset in device units into the buffer - * @param range The range in device units into the buffer - * @param buffer_type The precise type of buffer descriptor - */ -void vc_descriptor_set_writer_write_buffer(vc_ctx *ctx, vc_descriptor_set_writer *writer, u32 binding, u32 array_elt, vc_handle buffer, u64 offset, u64 range, VkDescriptorType buffer_type); - -/** - * @brief Writes an image type descriptor into the descriptor set - * - * @param ctx A vulcain context - * @param writer The writer - * @param binding The destination binding - * @param array_elt The destination array element - * @param view The image view (Can be VC_NULL_HANDLE) - * @param sampler A sampler (Can be VC_NULL_HANDLE) - * @param layout The layout in which the image will be when accessed/sampled - * @param image_type The precise type of image descriptor - */ -void vc_descriptor_set_writer_write_image(vc_ctx *ctx, vc_descriptor_set_writer *writer, u32 binding, u32 array_elt, vc_handle view, vc_handle sampler, VkImageLayout layout, VkDescriptorType image_type); - - -/** - * @brief Updates the descriptor set with the written information - * - * @param ctx A vulcain context - * @param writer The writer - * @param set The destination set - */ -void vc_descriptor_set_writer_write(vc_ctx *ctx, vc_descriptor_set_writer *writer, vc_descriptor_set set); - - -// ## PIPELINES ## - -typedef struct -{ - u32 set_layout_count; - vc_descriptor_set_layout *set_layouts; - - u32 push_constants_count; - VkPushConstantRange *push_constants; -} vc_pipeline_layout_info; - -typedef struct -{ - u8 *vertex_code; - u64 vertex_code_size; - const char *vertex_entry_point; - - u8 *fragment_code; - u64 fragment_code_size; - const char *fragment_entry_point; -} vc_gfx_pipeline_code_info; - -// - Vertex bindings -typedef struct -{ - u32 location; - VkFormat format; - u32 offset; -} vc_vertex_binding_attribute; - -typedef struct -{ - u32 binding; - u32 stride; - u32 attribute_count; - vc_vertex_binding_attribute *attributes; - - VkVertexInputRate input_rate; -} vc_vertex_binding; - -typedef struct -{ - - /** @brief The code of the programmable pipeline stages */ - vc_gfx_pipeline_code_info shader_code; - - vc_pipeline_layout_info layout_info; - - // Assembly state - u32 vertex_binding_count; - vc_vertex_binding *vertex_bindings; - - VkPrimitiveTopology topology; - - // Ignored if dynamic - u32 viewport_scissor_count; - VkViewport *viewports; - VkRect2D *scissors; - - // Depth state - b8 depth_test; - b8 depth_write; - VkCompareOp depth_compare_op; - b8 depth_bound_test_enable; - f32 depth_bounds_min; - f32 depth_bounds_max; - - // Stencil test - b8 stencil_test; - VkStencilOpState front_faces_stencil_op; - VkStencilOpState back_faces_stencil_op; - - b8 enable_depth_clamp; - VkPolygonMode polygon_mode; - VkCullModeFlagBits cull_mode; - VkFrontFace front_face; - f32 line_width; - - b8 enable_depth_bias; - f32 depth_bias_clamp; - f32 depth_bias_constant; - f32 depth_bias_slope; - - // Multisampling - VkSampleCountFlagBits sample_count; - b8 sample_shading; - f32 sample_shading_min_factor; - - // Attachment state - u32 attachment_count; - VkPipelineColorBlendAttachmentState *attachment_blends; - f32 blend_constants[4]; - - // Dynamic states - u32 dynamic_state_count; - VkDynamicState *dynamic_states; - -} vc_graphics_pipeline_desc; - -// For dynamic rendering -typedef struct -{ - uint32_t view_mask; - uint32_t color_attachment_count; - VkFormat *color_attachment_formats; - VkFormat depth_attachment_format; - VkFormat stencil_attachment_format; -} vc_pipeline_rendering_info; - -vc_gfx_pipeline vc_gfx_pipeline_dynamic_create( - vc_ctx *ctx, - vc_graphics_pipeline_desc desc, - vc_pipeline_rendering_info dyn_info - ); - -vc_compute_pipeline vc_compute_pipeline_create( - vc_ctx *ctx, - - u8 *code, - u64 code_size, - char *entry_point, - - vc_pipeline_layout_info layout_info - ); - -typedef enum -{ - VC_PIPELINE_COMPUTE = 1, - VC_PIPELINE_GRAPHICS, - VC_PIPELINE_TYPE_MAX, -} vc_pipeline_type; - -// ## DYNAMIC RENDERING ## - -/** - * @brief Represents an opaque "Command buffer recording context". The purpose is to accelerate frequent accesses to the same object in - * performance sensible operations. - */ - -typedef struct -{ - vc_image_view image_view; - VkImageLayout image_layout; - VkResolveModeFlagBits resolve_mode; - vc_image_view resolve_image_view; - VkImageLayout resolve_image_layout; - VkAttachmentLoadOp load_op; - VkAttachmentStoreOp store_op; - VkClearValue clear_value; -} vc_rendering_attachment_info; - -typedef struct -{ - VkRenderingFlags flags; - VkRect2D render_area; - u32 layer_count; - u32 view_mask; - u32 color_attachments_count; - vc_rendering_attachment_info *color_attachments; - vc_rendering_attachment_info *depth_attachment; - vc_rendering_attachment_info *stencil_attachment; -} vc_rendering_info; - - -// ## COMMAND BUFFERS ## -typedef uint64_t vc_cmd_record; - -void vc_command_buffer_submit(vc_ctx *ctx, vc_command_buffer buffer, vc_queue queue_submit, - u32 wait_sem_count, vc_semaphore *wait_sems, VkPipelineStageFlags *wait_stages, - u32 signal_sem_count, vc_semaphore *signal_sems); - -void vc_command_buffer_end(vc_cmd_record record); - -vc_cmd_record vc_command_buffer_begin(vc_ctx *ctx, vc_command_buffer cmd_buffer, VkCommandBufferUsageFlags usage); - -void vc_cmd_image_barrier(vc_cmd_record record, vc_image image, - VkPipelineStageFlags src_stages, VkPipelineStageFlags dst_stages, - VkAccessFlags src_access, VkAccessFlags dst_access, - VkImageLayout old_layout, VkImageLayout new_layout, - VkImageSubresourceRange subres_range, - vc_queue src_queue, vc_queue dst_queue - ); - -void vc_cmd_image_clear(vc_cmd_record record, vc_image image, - VkImageLayout layout, - VkClearColorValue clear_color, - VkImageSubresourceRange subres_range); - - -void vc_cmd_bind_descriptor_set(vc_cmd_record record, vc_handle pipeline, vc_descriptor_set set, u32 set_dest); -void vc_cmd_dispatch_compute(vc_cmd_record record, vc_compute_pipeline pipeline, u32 groups_x, u32 groups_y, u32 groups_z); -void vc_cmd_push_constants(vc_cmd_record record, vc_handle pipeline, VkShaderStageFlags stage, u32 offset, u32 size, void *data); - -void vc_cmd_draw(vc_cmd_record record, u32 vertex_count, u32 instance_count, u32 first_vertex, u32 first_instance); -void vc_cmd_bind_pipeline(vc_cmd_record record, vc_gfx_pipeline pipeline); - -// dynamic rendering -void vc_cmd_begin_rendering(vc_cmd_record record, vc_rendering_info info); -void vc_cmd_end_rendering(vc_cmd_record record); - -// ## IMGUI ## -void vc_imgui_setup(vc_ctx *ctx, vc_queue gui_queue, vc_windowing_system windowing_system, VkFormat image_formats); - -void vc_imgui_cleanup(vc_ctx *ctx); -void vc_cmd_imgui_end_frame_render(vc_cmd_record record, vc_image_view view, VkRect2D render_area, VkImageLayout layout); -void vc_imgui_begin_frame(vc_ctx *ctx); -#endif //__VULCAIN_H__ -