diff --git a/Cargo.toml b/Cargo.toml
index 1b3f910..0f90ea2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,3 +20,4 @@ cgmath = { version = "0.18.0", features = ["swizzle", "serde"] }
paste = "1.0.11"
serde = "1.0.152"
serde_json = "1.0.91"
+ringbuf = "0.3.2"
diff --git a/build.rs b/build.rs
index 1fadb6f..5586f94 100644
--- a/build.rs
+++ b/build.rs
@@ -10,8 +10,10 @@ const VK_HEADER: &[&str] = &[
const FN_PREFIX: &str = "PFN_";
const SHADER: &[&str] = &[
- "src/overlay/elements/pedals/single_color.vert",
- "src/overlay/elements/pedals/single_color.frag",
+ "src/overlay/elements/radar/single_color.vert",
+ "src/overlay/elements/radar/single_color.frag",
+ "src/overlay/elements/pedals/history.vert",
+ "src/overlay/elements/pedals/history.frag",
];
fn query_vulkan_function_typedefs() {
diff --git a/src/overlay/elements/leaderboard/leaderboard_grid.xml b/src/overlay/elements/leaderboard/leaderboard_grid.xml
index 65e6394..0ad24dc 100644
--- a/src/overlay/elements/leaderboard/leaderboard_grid.xml
+++ b/src/overlay/elements/leaderboard/leaderboard_grid.xml
@@ -1,5 +1,5 @@
-
\ No newline at end of file
diff --git a/src/overlay/elements/mod.rs b/src/overlay/elements/mod.rs
index bc1376d..df2b82b 100644
--- a/src/overlay/elements/mod.rs
+++ b/src/overlay/elements/mod.rs
@@ -7,3 +7,42 @@ pub use leaderboard::*;
pub use pedals::*;
pub use radar::*;
pub use watermark::*;
+
+#[derive(Clone)]
+pub struct PositionOnlyVertex {
+ pub position: cgmath::Vector4,
+}
+
+impl PositionOnlyVertex {
+ ///
+ /// corners[0] - bottom left
+ /// corners[1] - top left
+ /// corners[2] - top right
+ /// corners[3] - bottom right
+ ///
+ pub fn from_2d_corners(
+ ortho: cgmath::Matrix4,
+ corners: [cgmath::Vector2; 4],
+ ) -> [Self; 6] {
+ [
+ Self {
+ position: ortho * corners[0].extend(0.0).extend(1.0),
+ },
+ Self {
+ position: ortho * corners[1].extend(0.0).extend(1.0),
+ },
+ Self {
+ position: ortho * corners[2].extend(0.0).extend(1.0),
+ },
+ Self {
+ position: ortho * corners[2].extend(0.0).extend(1.0),
+ },
+ Self {
+ position: ortho * corners[3].extend(0.0).extend(1.0),
+ },
+ Self {
+ position: ortho * corners[0].extend(0.0).extend(1.0),
+ },
+ ]
+ }
+}
diff --git a/src/overlay/elements/pedals/history.frag b/src/overlay/elements/pedals/history.frag
new file mode 100644
index 0000000..07bd8fa
--- /dev/null
+++ b/src/overlay/elements/pedals/history.frag
@@ -0,0 +1,12 @@
+#version 450
+
+layout (set = 0, binding = 0) uniform Color {
+ vec4 val;
+} color;
+
+layout (location = 0) out vec4 out_color;
+
+void main()
+{
+ out_color = color.val;
+}
diff --git a/src/overlay/elements/pedals/history.vert b/src/overlay/elements/pedals/history.vert
new file mode 100644
index 0000000..126ef86
--- /dev/null
+++ b/src/overlay/elements/pedals/history.vert
@@ -0,0 +1,8 @@
+#version 450
+
+layout (location = 0) in vec4 position;
+
+void main()
+{
+ gl_Position = position;
+}
\ No newline at end of file
diff --git a/src/overlay/elements/pedals/mod.rs b/src/overlay/elements/pedals/mod.rs
index 51ccbb6..c76a07f 100644
--- a/src/overlay/elements/pedals/mod.rs
+++ b/src/overlay/elements/pedals/mod.rs
@@ -1,45 +1,230 @@
-use std::sync::Arc;
+mod pipeline;
+
+use std::sync::{Arc, Mutex};
use anyhow::Result;
+use cgmath::{ortho, vec4, Matrix4};
use rfactor_sm_reader::{rF2VehicleTelemetry, VehicleScoringInfoV01};
+use ringbuf::{HeapRb, Rb};
use ui::prelude::*;
+use vulkan_rs::prelude::*;
use crate::overlay::{
rfactor_data::{DataReceiver, GamePhase},
UiOverlay,
};
+use crate::write_log;
+
+use self::pipeline::HistoryPipeline;
+
+use super::PositionOnlyVertex;
pub struct Pedals {
gui: Arc,
brake: Arc,
throttle: Arc,
- history: Arc,
+ _history: Arc,
- throttle_samples: Vec,
- brake_samples: Vec,
+ throttle_samples: HeapRb,
+ brake_samples: HeapRb,
+
+ ortho: Matrix4,
+
+ device: Arc,
+ queue: Arc>,
+
+ render_target: RenderTarget,
+ pipeline: HistoryPipeline,
+
+ brake_descriptor: Arc,
+ brake_vertex_buffer: Arc>,
+ throttle_descriptor: Arc,
+ throttle_vertex_buffer: Arc>,
}
impl Pedals {
- pub fn new(gui_handler: &Arc) -> Result {
+ pub fn new(
+ gui_handler: &Arc,
+ device: Arc,
+ queue: Arc>,
+ ) -> Result {
const DESC: &str = include_str!("pedals.xml");
let gui = GuiBuilder::from_str(gui_handler, DESC)?;
let brake = gui.element("brake")?;
let throttle = gui.element("throttle")?;
- let history = gui.element("history")?;
+ let history: Arc = gui.element("history")?;
- Ok(Self {
+ let (icon_width, icon_height) = history.extent();
+ let history_image = Image::empty(
+ icon_width as u32,
+ icon_height as u32,
+ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
+ VK_SAMPLE_COUNT_1_BIT,
+ )
+ .format(VK_FORMAT_R8G8B8A8_UNORM)
+ .attach_sampler(Sampler::nearest_sampler().build(&device)?)
+ .build(&device, &queue)?;
+
+ history_image.convert_layout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)?;
+
+ history.set_icon(&history_image)?;
+
+ let render_target = RenderTarget::builder()
+ .add_sub_pass(
+ SubPass::builder(history_image.width(), history_image.height())
+ .set_prepared_targets(&[history_image.clone()], 0, [0.3, 0.3, 0.3, 1.0], true)
+ .build(&device, &queue)?,
+ )
+ .build(&device)?;
+
+ let pipeline = HistoryPipeline::new(
+ device.clone(),
+ render_target.render_pass(),
+ history_image.width(),
+ history_image.height(),
+ )?;
+
+ let ortho = ortho(0.0, history_image.width() as f32, 0.0, 1.0, -1.0, 1.0);
+
+ let descriptor_pool = DescriptorPool::builder()
+ .set_layout(pipeline.descriptor_layout().clone())
+ .set_descriptor_set_count(2)
+ .build(device.clone())?;
+
+ let brake_color_buffer: Arc> = Buffer::builder()
+ .set_usage(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT)
+ .set_memory_usage(MemoryUsage::CpuOnly)
+ .set_data(&[0.9, 0.0, 0.0, 1.0])
+ .build(device.clone())?;
+
+ write_log!("allocate brake descriptor");
+
+ let brake_descriptor = descriptor_pool.prepare_set().allocate()?;
+ brake_descriptor.update(&[DescriptorWrite::uniform_buffers(0, &[&brake_color_buffer])])?;
+
+ let throttle_color_buffer: Arc> = Buffer::builder()
+ .set_usage(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT)
+ .set_memory_usage(MemoryUsage::CpuOnly)
+ .set_data(&[0.0, 0.9, 0.0, 1.0])
+ .build(device.clone())?;
+
+ write_log!("allocate throttle descriptor");
+
+ let throttle_descriptor = descriptor_pool.prepare_set().allocate()?;
+ throttle_descriptor.update(&[DescriptorWrite::uniform_buffers(
+ 0,
+ &[&throttle_color_buffer],
+ )])?;
+
+ let mut throttle_samples = HeapRb::new(icon_width as usize);
+ let mut brake_samples = HeapRb::new(icon_width as usize);
+
+ for _ in 0..icon_width {
+ throttle_samples.push_overwrite(0.0);
+ brake_samples.push_overwrite(0.0);
+ }
+
+ let brake_vertex_buffer = Self::create_vertex_buffer(&device, icon_width as VkDeviceSize)?;
+ let throttle_vertex_buffer =
+ Self::create_vertex_buffer(&device, icon_width as VkDeviceSize)?;
+
+ let me = Self {
gui,
brake,
throttle,
- history,
+ _history: history,
- throttle_samples: Vec::new(),
- brake_samples: Vec::new(),
- })
+ throttle_samples,
+ brake_samples,
+
+ ortho,
+
+ device,
+ queue,
+
+ render_target,
+ pipeline,
+
+ brake_descriptor,
+ brake_vertex_buffer,
+ throttle_descriptor,
+ throttle_vertex_buffer,
+ };
+
+ me.update_vertex_buffers()?;
+
+ Ok(me)
+ }
+
+ fn create_vertex_buffer(
+ device: &Arc,
+ size: VkDeviceSize,
+ ) -> Result>> {
+ Buffer::builder()
+ .set_usage(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT)
+ .set_memory_usage(MemoryUsage::CpuOnly)
+ .set_size(size)
+ .build(device.clone())
+ }
+
+ fn update_vertex_buffers(&self) -> Result<()> {
+ self.update_vertex_buffer(
+ &self.throttle_vertex_buffer,
+ self.throttle_samples.as_slices(),
+ )?;
+ self.update_vertex_buffer(&self.brake_vertex_buffer, self.brake_samples.as_slices())?;
+
+ Ok(())
+ }
+
+ fn update_vertex_buffer(
+ &self,
+ buffer: &Arc>,
+ (data1, data2): (&[f32], &[f32]),
+ ) -> Result<()> {
+ let points = data1
+ .iter()
+ .chain(data2.iter())
+ .enumerate()
+ .map(|(x, &date)| PositionOnlyVertex {
+ position: self.ortho * vec4(x as f32, date, 0.0, 1.0),
+ })
+ .collect::>();
+
+ buffer.fill(&points)
+ }
+
+ pub fn render(&self) -> Result> {
+ let command_buffer =
+ CommandBuffer::new_primary().build(self.device.clone(), self.queue.clone())?;
+
+ {
+ let mut recorder = command_buffer.begin(VkCommandBufferBeginInfo::new(
+ VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT
+ | VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT,
+ ))?;
+
+ self.render_target
+ .begin(&recorder, VK_SUBPASS_CONTENTS_INLINE, 0);
+
+ recorder.bind_pipeline(self.pipeline.pipeline())?;
+
+ recorder.bind_descriptor_sets_minimal(&[&self.throttle_descriptor]);
+ recorder.bind_vertex_buffer(&self.throttle_vertex_buffer);
+ recorder.draw_complete_single_instance(self.throttle_vertex_buffer.size() as u32);
+
+ recorder.bind_descriptor_sets_minimal(&[&self.brake_descriptor]);
+ recorder.bind_vertex_buffer(&self.brake_vertex_buffer);
+ recorder.draw_complete_single_instance(self.brake_vertex_buffer.size() as u32);
+
+ self.render_target.end(&recorder);
+ }
+
+ Ok(command_buffer)
}
}
@@ -70,8 +255,10 @@ impl DataReceiver for Pedals {
self.throttle.set_progress(throttle)?;
self.brake.set_progress(brake)?;
- self.throttle_samples.push(throttle);
- self.brake_samples.push(brake);
+ self.throttle_samples.push_overwrite(throttle);
+ self.brake_samples.push_overwrite(brake);
+
+ self.update_vertex_buffers()?;
}
}
None => {
@@ -82,3 +269,28 @@ impl DataReceiver for Pedals {
Ok(())
}
}
+
+#[cfg(test)]
+mod test {
+ use ringbuf::{HeapRb, Rb};
+
+ #[test]
+ fn rb_test() {
+ const CAP: usize = 10;
+
+ let mut buf = HeapRb::new(CAP);
+
+ for _ in 0..CAP {
+ buf.push_overwrite(20);
+ }
+
+ println!("{:?}", buf.as_slices());
+
+ buf.push_overwrite(40);
+ buf.push_overwrite(40);
+ buf.push_overwrite(40);
+ buf.push_overwrite(40);
+
+ println!("{:?}", buf.as_slices());
+ }
+}
diff --git a/src/overlay/elements/pedals/pedals.xml b/src/overlay/elements/pedals/pedals.xml
index a05d5f7..feaf73c 100644
--- a/src/overlay/elements/pedals/pedals.xml
+++ b/src/overlay/elements/pedals/pedals.xml
@@ -1,6 +1,6 @@
-
@@ -8,7 +8,7 @@
x_slot="1" y_slot="0" y_size="2" background="#494949" direction="bottom_to_top"
foreground="#00b900">
-
\ No newline at end of file
diff --git a/src/overlay/elements/pedals/pipeline.rs b/src/overlay/elements/pedals/pipeline.rs
new file mode 100644
index 0000000..49bfe86
--- /dev/null
+++ b/src/overlay/elements/pedals/pipeline.rs
@@ -0,0 +1,102 @@
+use anyhow::Result;
+use vulkan_rs::prelude::*;
+
+use std::{mem, sync::Arc};
+
+use super::super::PositionOnlyVertex;
+
+pub struct HistoryPipeline {
+ pipeline: Arc,
+ descriptor_layout: Arc,
+}
+
+impl HistoryPipeline {
+ pub fn new(
+ device: Arc,
+ renderpass: &Arc,
+ width: u32,
+ height: u32,
+ ) -> Result {
+ let vertex_shader = ShaderModule::from_slice(
+ device.clone(),
+ include_bytes!("history.vert.spv"),
+ ShaderType::Vertex,
+ )?;
+ let fragment_shader = ShaderModule::from_slice(
+ device.clone(),
+ include_bytes!("history.frag.spv"),
+ ShaderType::Fragment,
+ )?;
+
+ let descriptor_layout = DescriptorSetLayout::builder()
+ .add_layout_binding(
+ 0,
+ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+ VK_SHADER_STAGE_FRAGMENT_BIT,
+ 0,
+ )
+ .build(device.clone())?;
+
+ let pipeline_layout = PipelineLayout::builder()
+ .add_descriptor_set_layout(&descriptor_layout)
+ .build(device.clone())?;
+
+ let viewport = VkViewport {
+ x: 0.0,
+ y: 0.0,
+ width: width as f32,
+ height: height as f32,
+ minDepth: 0.0,
+ maxDepth: 1.0,
+ };
+
+ let scissor = VkRect2D {
+ offset: VkOffset2D { x: 0, y: 0 },
+ extent: VkExtent2D {
+ width: width,
+ height: height,
+ },
+ };
+
+ let pipeline = Pipeline::new_graphics()
+ .set_vertex_shader(
+ vertex_shader.clone(),
+ vec![VkVertexInputBindingDescription {
+ binding: 0,
+ stride: mem::size_of::() as u32,
+ inputRate: VK_VERTEX_INPUT_RATE_VERTEX,
+ }],
+ vec![
+ // position
+ VkVertexInputAttributeDescription {
+ location: 0,
+ binding: 0,
+ format: VK_FORMAT_R32G32B32A32_SFLOAT,
+ offset: 0,
+ },
+ ],
+ )
+ .set_fragment_shader(fragment_shader.clone())
+ .input_assembly(VK_PRIMITIVE_TOPOLOGY_LINE_STRIP, false)
+ .default_depth_stencil(false, false)
+ .default_color_blend(vec![VkPipelineColorBlendAttachmentState::default()])
+ .default_rasterization(VK_CULL_MODE_NONE, VK_FRONT_FACE_COUNTER_CLOCKWISE)
+ .default_multisample(VK_SAMPLE_COUNT_1_BIT)
+ .add_viewport(viewport)
+ .add_scissor(scissor)
+ .build(device, &pipeline_layout, &renderpass, 0)?;
+
+ Ok(Self {
+ descriptor_layout,
+ pipeline,
+ })
+ }
+
+ pub fn pipeline(&self) -> &Arc {
+ &self.pipeline
+ }
+
+ pub fn descriptor_layout(&self) -> &Arc {
+ &self.descriptor_layout
+ }
+}
diff --git a/src/overlay/elements/radar/mod.rs b/src/overlay/elements/radar/mod.rs
index 7b2b0f2..fb94c36 100644
--- a/src/overlay/elements/radar/mod.rs
+++ b/src/overlay/elements/radar/mod.rs
@@ -1,7 +1,7 @@
mod pipeline;
use anyhow::Result;
-use cgmath::{ortho, vec2, vec3, vec4, Deg, InnerSpace, Matrix4, Rad, Vector2, Vector3, Vector4};
+use cgmath::{ortho, vec2, vec3, vec4, Deg, InnerSpace, Matrix4, Rad, Vector2, Vector3};
use rfactor_sm_reader::*;
use serde::{Deserialize, Serialize};
use vulkan_rs::prelude::*;
@@ -19,41 +19,7 @@ use crate::{
write_log,
};
-#[derive(Clone)]
-pub struct PositionOnlyVertex {
- pub position: Vector4,
-}
-
-impl PositionOnlyVertex {
- ///
- /// corners[0] - bottom left
- /// corners[1] - top left
- /// corners[2] - top right
- /// corners[3] - bottom right
- ///
- pub fn from_2d_corners(ortho: Matrix4, corners: [Vector2; 4]) -> [Self; 6] {
- [
- Self {
- position: ortho * corners[0].extend(0.0).extend(1.0),
- },
- Self {
- position: ortho * corners[1].extend(0.0).extend(1.0),
- },
- Self {
- position: ortho * corners[2].extend(0.0).extend(1.0),
- },
- Self {
- position: ortho * corners[2].extend(0.0).extend(1.0),
- },
- Self {
- position: ortho * corners[3].extend(0.0).extend(1.0),
- },
- Self {
- position: ortho * corners[0].extend(0.0).extend(1.0),
- },
- ]
- }
-}
+use super::PositionOnlyVertex;
fn convert_vec(v: rF2Vec3) -> Vector3 {
vec3(v.x as f32, v.y as f32, v.z as f32)
diff --git a/src/overlay/elements/watermark/mod.rs b/src/overlay/elements/watermark/mod.rs
index 7eaec0b..093b89f 100644
--- a/src/overlay/elements/watermark/mod.rs
+++ b/src/overlay/elements/watermark/mod.rs
@@ -10,7 +10,7 @@ use crate::overlay::{
};
pub struct Watermark {
- _gui: Arc,
+ gui: Arc,
}
impl Watermark {
@@ -19,9 +19,7 @@ impl Watermark {
let gui = GuiBuilder::from_str(gui_handler, DESC)?;
- gui.enable()?;
-
- Ok(Self { _gui: gui })
+ Ok(Self { gui })
}
}
@@ -30,9 +28,14 @@ impl UiOverlay for Watermark {}
impl DataReceiver for Watermark {
fn scoring_update(
&mut self,
- _phase: GamePhase,
+ phase: GamePhase,
_vehicle_scoring: &[VehicleScoringInfoV01],
) -> Result<()> {
+ match phase {
+ GamePhase::TestDay => self.gui.enable()?,
+ _ => self.gui.disable()?,
+ }
+
Ok(())
}
diff --git a/src/overlay/mod.rs b/src/overlay/mod.rs
index 82e55ab..09a26ce 100644
--- a/src/overlay/mod.rs
+++ b/src/overlay/mod.rs
@@ -142,7 +142,7 @@ impl Overlay {
// only font is used
let mut create_info = GuiHandlerCreateInfo::default();
- create_info.font_path = AssetPath::from("/usr/share/vulkan_rf2_layer/font.png");
+ create_info.font_path = AssetPath::from("/opt/sata_ssd/Workspace/vk_layer_rs/font.png");
create_info.font_path.assume_prefix_free();
// required to not crash
@@ -196,8 +196,15 @@ impl Overlay {
// create pedals
if self.config.ui_config.enable_pedals {
- let pedals = Rc::new(RefCell::new(Pedals::new(&gui_handler)?));
- self.ui_elements.push(pedals);
+ let pedals = Rc::new(RefCell::new(Pedals::new(
+ &gui_handler,
+ self.device(),
+ self.queue(),
+ )?));
+
+ self.ui_elements.push(pedals.clone());
+
+ rendering.add_render_callback(move |_| pedals.borrow().render());
write_log!("Pedals successfully created");
}
diff --git a/src/overlay/rfactor_data.rs b/src/overlay/rfactor_data.rs
index d65e62a..32fc85d 100644
--- a/src/overlay/rfactor_data.rs
+++ b/src/overlay/rfactor_data.rs
@@ -109,6 +109,8 @@ impl RFactorData {
let phase = GamePhase::try_from(scoring_info.mSession)?;
+ write_log!(format!("GamePhase: {:?}", phase));
+
for receiver in self.receivers.iter() {
receiver
.borrow_mut()