UDP reception

This commit is contained in:
2025-10-09 17:56:51 +02:00
parent 391c5e5c7c
commit ebf6814651
4 changed files with 222 additions and 717 deletions

15
Cargo.lock generated
View File

@ -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]]

View File

@ -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"] }

View File

@ -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<Vec<u8>>,
data_sender: Sender<Vec<u8>>,
samples_sender: Sender<f32>,
}
struct Transceiver {}
impl Transceiver
{
pub async fn start<T: SampleSender>(sample_sender: T) -> Self
{
let (transmitter_tx, transmitter_rx) = channel::<Vec<u8>>(4096);
let (acknowledged_tx, acknowledged_rx) = channel::<()>(32);
let (ack_tx, ack_rx) = channel::<()>(32);
let (samples_tx, samples_rx) = channel::<f32>(4096);
let (receiver_tx, receiver_rx) = channel::<Vec<u8>>(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<T: SampleSender>(mut acknowledged: Receiver<()>, mut data_receiver: Receiver<Vec<u8>>, mut ack_receiver: Receiver<()>, mut samples_sender: T)
{
let mut send_queue: VecDeque<Frame> = 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<T: SampleSender>(
mut sample_stream: Receiver<f32>,
mut tx_stream: Receiver<Vec<u8>>,
mut rx_stream: Sender<Vec<u8>>,
sample_sender: &mut T,
) {
let mut resend: Option<Vec<u8>> = 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<f32>, data_sender: Sender<Vec<u8>>, ack_sender: Sender<()>)
{
async fn squelch_detector(sample_stream: &mut Receiver<f32>) {
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<T: SampleSender>(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<f32>) -> Result<Frame, FrameConstructionError> {
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::<Vec<_>>());
let mut neg_correllator = FIRFilter::new(&neg_ir.collect::<Vec<_>>());
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<u32> = 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<u8>),
Ack
Ack,
}
type FrameConstructionError = ();
pub struct FrameConstructor
{
pub struct FrameConstructor {
frame: Vec<u8>,
frame_countdown: Option<u16>,
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<Option<Frame>, FrameConstructionError>
{
if self.frame.is_empty() && byte != 0xC4 && byte != 0x4C
{
return Err(());
pub fn add_byte(&mut self, byte: u8) -> Result<Option<Frame>, 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<u8>
{
impl Frame {
pub fn bytes(&self) -> Vec<u8> {
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::<f32>(1024);
Self {
let (transmit_tx, transmit_rx) = channel::<Vec<u8>>(1024);
let (receive_tx, receive_rx) = channel::<Vec<u8>>(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| {});
}
}

544
vulcain.h
View File

@ -1,544 +0,0 @@
#ifndef __VULCAIN_H__
#define __VULCAIN_H__
#include "vc_windowing.h"
#include "handles/vc_handles.h"
#include <vulkan/vulkan.h>
#include <stdbool.h>
#include <stdint.h>
// ## TODO: Make those header private
#include <vk_mem_alloc.h>
#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__