UDP reception
This commit is contained in:
15
Cargo.lock
generated
15
Cargo.lock
generated
@ -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]]
|
||||
|
||||
@ -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"] }
|
||||
|
||||
378
src/main.rs
378
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<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
544
vulcain.h
@ -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__
|
||||
|
||||
Reference in New Issue
Block a user