use vulkano::descriptor::descriptor_set::PersistentDescriptorSet; use rusttype::{Font, PositionedGlyph, Scale, Rect, point}; use rusttype::gpu_cache::Cache; use vulkano::buffer::{BufferAccess, BufferUsage, ImmutableBuffer, CpuAccessibleBuffer}; use vulkano::device::{Device, Queue}; use std::sync::Arc; use vulkano::command_buffer::{AutoCommandBufferBuilder, DynamicState}; use vulkano::image::{ImmutableImage, ImageUsage, ImageLayout, Dimensions}; use vulkano::format::ClearValue; use vulkano::format::Format::R8Unorm; /* So I think this thing is going to build text vertex buffers to send to the GPU. I assume I will just lay them out in ASCII for now along with a list of transformation matrices Glpyh: index: 0-255, scale: 0.0 - 99.99 transform: (0.0, 0.0) - (1.0, 1.0) I'm not sure if I want to send a new transformation matrix for each frame. But I suppose that can come when I look at caching the sprites */ pub struct Glyph { } /// Typed wrapper for a u32 shader handle (index id) #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] pub struct CanvasFontHandle { pub handle: u32 } /// So currently, I'm using these as container classes which vkprocessor owns /// I then use a CanvasFrame which accumulates lists of handles and vertices. pub struct CanvasText { device: Arc, queue: Arc, font: Font<'static>, } impl CanvasText { /// Load the font pub fn new(device: Arc, queue: Arc) -> CanvasText { let font_data = include_bytes!("../../resources/fonts/sansation.ttf"); let font = Font::from_bytes(font_data as &[u8]).unwrap(); CanvasText { device: device.clone(), queue: queue.clone(), font: font, } } /// Generate a vertex buffer from the font /* So... These fonts are going to have unequal amounts of vertices. */ pub fn get_vertex_buffer(&self) { unimplemented!() } /// postpone this until caching pub fn queue_text(&mut self, x: f32, y: f32, size: f32, color: [f32; 4], text: &str) { let glyphs: Vec = self.font.layout(text, Scale::uniform(size), point(x, y)).map(|x| x.standalone()).collect(); for glyph in &glyphs { self.cache.queue_glyph(0, glyph.clone()); } // self.texts.push(TextData { // glyphs: glyphs.clone(), // color: color, // }); } pub fn draw_text(&mut self, command_buffer: AutoCommandBufferBuilder, image_num: usize ) -> AutoCommandBufferBuilder { // let screen_width = 0; // let screen_height = 0; // // let cache_pixel_buffer = &mut self.cache_pixel_buffer; // let cache = &mut self.cache; // // // update texture cache // cache.cache_queued( // |rect, src_data| { // let width = (rect.max.x - rect.min.x) as usize; // let height = (rect.max.y - rect.min.y) as usize; // let mut dst_index = rect.min.y as usize * CACHE_WIDTH + rect.min.x as usize; // let mut src_index = 0; // // for _ in 0..height { // let dst_slice = &mut cache_pixel_buffer[dst_index..dst_index+width]; // let src_slice = &src_data[src_index..src_index+width]; // dst_slice.copy_from_slice(src_slice); // // dst_index += CACHE_WIDTH; // src_index += width; // } // } // ).unwrap(); // // // need to get a hold of the cache buffer handle after I create it // // will then get swapped into this texture buffer // // Hmmm so this uninit call returns the texture and then a handle for whatever fills it up // let (cache_texture, cache_texture_write) = ImmutableImage::uninitialized( // self.device.clone(), // Dimensions::Dim2d { width: CACHE_WIDTH as u32, height: CACHE_HEIGHT as u32 }, // R8Unorm, // 1, // ImageUsage { // sampled: true, // transfer_destination: true, // .. ImageUsage::none() // }, // ImageLayout::General, // Some(self.queue.family()) // ).unwrap(); // // // // let set = Arc::new( // PersistentDescriptorSet::start(self.pipeline.clone(), 0) // .add_sampled_image(cache_texture.clone(), sampler).unwrap() // .build().unwrap() // ); // // let mut command_buffer = command_buffer // .copy_buffer_to_image( // buffer.clone(), // cache_texture_write, // ).unwrap() // .begin_render_pass(self.framebuffers[image_num].clone(), false, vec!(ClearValue::None)).unwrap(); // // // draw // for text in &mut self.texts.drain(..) { // let vertices: Vec = text.glyphs.iter().flat_map(|g| { // if let Ok(Some((uv_rect, screen_rect))) = cache.rect_for(0, g) { // let gl_rect = Rect { // min: point( // (screen_rect.min.x as f32 / screen_width as f32 - 0.5) * 2.0, // (screen_rect.min.y as f32 / screen_height as f32 - 0.5) * 2.0 // ), // max: point( // (screen_rect.max.x as f32 / screen_width as f32 - 0.5) * 2.0, // (screen_rect.max.y as f32 / screen_height as f32 - 0.5) * 2.0 // ) // }; // vec!( //// Vertex { //// position: [gl_rect.min.x, gl_rect.max.y], //// tex_position: [uv_rect.min.x, uv_rect.max.y], //// color: text.color, //// }, //// Vertex { //// position: [gl_rect.min.x, gl_rect.min.y], //// tex_position: [uv_rect.min.x, uv_rect.min.y], //// color: text.color, //// }, //// Vertex { //// position: [gl_rect.max.x, gl_rect.min.y], //// tex_position: [uv_rect.max.x, uv_rect.min.y], //// color: text.color, //// }, //// //// Vertex { //// position: [gl_rect.max.x, gl_rect.min.y], //// tex_position: [uv_rect.max.x, uv_rect.min.y], //// color: text.color, //// }, //// Vertex { //// position: [gl_rect.max.x, gl_rect.max.y], //// tex_position: [uv_rect.max.x, uv_rect.max.y], //// color: text.color, //// }, //// Vertex { //// position: [gl_rect.min.x, gl_rect.max.y], //// tex_position: [uv_rect.min.x, uv_rect.max.y], //// color: text.color, //// }, // ).into_iter() // } // else { // vec!().into_iter() // } // }).collect(); // // let vertex_buffer = CpuAccessibleBuffer::from_iter(self.device.clone(), BufferUsage::all(), vertices.into_iter()).unwrap(); // command_buffer = command_buffer.draw(self.pipeline.clone(), &DynamicState::none(), vertex_buffer.clone(), set.clone(), ()).unwrap(); // } command_buffer//.end_render_pass().unwrap() } }