diff --git a/.gitignore b/.gitignore index cb54759..64cf459 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,6 @@ /Cargo.lock *.spv +image_to_file_*.png vk_functions \ No newline at end of file diff --git a/build.rs b/build.rs index 5586f94..dbbfd6d 100644 --- a/build.rs +++ b/build.rs @@ -14,6 +14,8 @@ const SHADER: &[&str] = &[ "src/overlay/elements/radar/single_color.frag", "src/overlay/elements/pedals/history.vert", "src/overlay/elements/pedals/history.frag", + "src/overlay/elements/leaderboard/generator.frag", + "src/overlay/elements/leaderboard/generator.vert", ]; fn query_vulkan_function_typedefs() { diff --git a/src/overlay/elements/leaderboard/bg_generator.rs b/src/overlay/elements/leaderboard/bg_generator.rs new file mode 100644 index 0000000..f0cc220 --- /dev/null +++ b/src/overlay/elements/leaderboard/bg_generator.rs @@ -0,0 +1,229 @@ +use anyhow::Result; +use cgmath::{ortho, vec2, Deg}; +use vulkan_rs::prelude::*; + +use std::{ + mem, + sync::{Arc, Mutex}, + time::Duration, +}; + +use crate::overlay::elements::PositionOnlyVertex; + +pub struct BackgroundGenerator; + +impl BackgroundGenerator { + pub fn generate( + device: &Arc, + queue: &Arc>, + image_infos: [(u32, u32); N], + ) -> Result<[Arc; N]> { + Ok(image_infos + .iter() + .map(|(width, height)| { + let image = Image::empty( + *width as u32, + *height as u32, + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT + | VK_IMAGE_USAGE_SAMPLED_BIT + | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, + VK_SAMPLE_COUNT_1_BIT, + ) + .format(VK_FORMAT_R8G8B8A8_UNORM) + .attach_sampler(Sampler::nearest_sampler().build(&device)?) + .build(&device, &queue)?; + + image.convert_layout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)?; + + let render_target = RenderTarget::builder() + .add_sub_pass( + SubPass::builder(image.width(), image.height()) + .set_prepared_targets(&[image.clone()], 0, [0.0, 0.0, 0.0, 1.0], true) + .build(&device, &queue)?, + ) + .build(&device)?; + + let vertex_shader = ShaderModule::from_slice( + device.clone(), + include_bytes!("generator.vert.spv"), + ShaderType::Vertex, + )?; + let fragment_shader = ShaderModule::from_slice( + device.clone(), + include_bytes!("generator.frag.spv"), + ShaderType::Fragment, + )?; + + let viewport = VkViewport { + x: 0.0, + y: 0.0, + width: image.width() as f32, + height: image.height() as f32, + minDepth: 0.0, + maxDepth: 1.0, + }; + + let scissor = VkRect2D { + offset: VkOffset2D { x: 0, y: 0 }, + extent: VkExtent2D { + width: image.width(), + height: image.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_TRIANGLE_LIST, 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.clone(), + &PipelineLayout::builder().build(device.clone())?, + render_target.render_pass(), + 0, + )?; + + let ortho = ortho( + 0.0, + image.width() as f32, + 0.00, + image.height() as f32, + 0.0, + 1.0, + ); + + let command_buffer = + CommandBuffer::new_primary().build(device.clone(), queue.clone())?; + + // let corners = [ + // vec2(0.0, 0.0), + // vec2(0.0, image.height() as f32), + // vec2(image.width() as f32, image.height() as f32), + // vec2(image.width() as f32, 0.0), + // ]; + + // angle 70° + let angle: Deg = Deg(70.0); + let tan = angle.0.atan(); + + let corners = [ + vec2(0.0, 0.0), + vec2((image.height() as f32) / tan, image.height() as f32), + vec2(image.width() as f32, image.height() as f32), + vec2(image.width() as f32 - (image.height() as f32) / tan, 0.0), + ]; + + println!("width: {} height: {}", image.width(), image.height()); + + for corner in corners.iter() { + println!("corner: {:?}", corner); + } + + let vertices = PositionOnlyVertex::from_2d_corners(ortho, corners); + + let vertex_buffer = Buffer::builder() + .set_usage(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT) + .set_memory_usage(MemoryUsage::CpuOnly) + .set_data(&vertices) + .build(device.clone())?; + + SingleSubmit::builder(&command_buffer, queue, |recorder| { + render_target.begin(recorder, VK_SUBPASS_CONTENTS_INLINE, 0); + + recorder.bind_pipeline(&pipeline)?; + + recorder.bind_vertex_buffer(&vertex_buffer); + recorder.draw_complete_single_instance(vertex_buffer.size() as u32); + + render_target.end(recorder); + + Ok(()) + }) + .wait_for_timeout(Duration::from_secs(1)) + .submit()?; + + Ok(image) + }) + .collect::>>>()? + .try_into() + .unwrap_or_else(|_: Vec>| { + unreachable!("create array from vec from an array") + })) + } +} + +#[cfg(test)] +mod test { + use anyhow::Result; + use vulkan_rs::prelude::*; + + use std::sync::{Arc, Mutex}; + + use super::BackgroundGenerator; + + fn create_vk_handles() -> Result<(Arc, Arc>)> { + let instance = Instance::new( + VkApplicationInfo::new( + &VkString::new("Test: image::to_file"), + 1, + &VkString::new("no name"), + 1, + VK_MAKE_VERSION(1, 3, 0), + ), + VulkanDebugInfo::default(), + InstanceExtensions::default(), + )?; + + let physical_device = PhysicalDevice::new(instance)?; + + let queue_info = Queue::create_non_presentable_request_info( + &physical_device, + VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_TRANSFER_BIT | VK_QUEUE_COMPUTE_BIT, + )?; + + let device = Device::new( + physical_device, + DeviceExtensions::default(), + &[queue_info.queue_create_info], + DeviceFeatures::default(), + )?; + + let queue = device.get_queue(queue_info.queue_family_index, queue_info.queue_index); + + Ok((device, queue)) + } + + #[test] + fn generate_image_test() { + let (device, queue) = create_vk_handles().unwrap(); + + let images = BackgroundGenerator::generate(&device, &queue, [(120, 40)]).unwrap(); + + for (index, image) in images.iter().enumerate() { + image + .to_file(&format!("image_to_file_{}.png", index)) + .unwrap() + } + } +} diff --git a/src/overlay/elements/leaderboard/generator.frag b/src/overlay/elements/leaderboard/generator.frag new file mode 100644 index 0000000..72ade95 --- /dev/null +++ b/src/overlay/elements/leaderboard/generator.frag @@ -0,0 +1,8 @@ +#version 450 + +layout (location = 0) out vec4 out_color; + +void main() +{ + out_color = vec4(1.0, 1.0, 1.0, 1.0); +} diff --git a/src/overlay/elements/leaderboard/generator.vert b/src/overlay/elements/leaderboard/generator.vert new file mode 100644 index 0000000..126ef86 --- /dev/null +++ b/src/overlay/elements/leaderboard/generator.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/leaderboard/mod.rs b/src/overlay/elements/leaderboard/mod.rs index 08ac766..8b79120 100644 --- a/src/overlay/elements/leaderboard/mod.rs +++ b/src/overlay/elements/leaderboard/mod.rs @@ -1,3 +1,4 @@ +mod bg_generator; mod leaderboard_entry; use leaderboard_entry::*; diff --git a/src/overlay/elements/pedals/mod.rs b/src/overlay/elements/pedals/mod.rs index c4e057d..37134f9 100644 --- a/src/overlay/elements/pedals/mod.rs +++ b/src/overlay/elements/pedals/mod.rs @@ -89,7 +89,7 @@ impl Pedals { history_image.height(), )?; - let ortho = ortho(0.0, history_image.width() as f32, -0.01, 1.01, -1.0, 1.0); + let ortho = ortho(0.0, history_image.width() as f32, -0.01, 1.01, 0.0, 1.0); let descriptor_pool = DescriptorPool::builder() .set_layout(pipeline.descriptor_layout().clone()) diff --git a/src/overlay/elements/radar/mod.rs b/src/overlay/elements/radar/mod.rs index f0bc4bb..e4e0fd2 100644 --- a/src/overlay/elements/radar/mod.rs +++ b/src/overlay/elements/radar/mod.rs @@ -105,7 +105,7 @@ impl Radar { rendering.swapchain().width() as f32, 0.0, rendering.swapchain().height() as f32, - -1.0, + 0.0, 1.0, );