fiddling with text rendering
This commit is contained in:
@@ -21,4 +21,5 @@ shaderc = "0.5.0"
|
|||||||
shade_runner = {path = "../shade_runner"}
|
shade_runner = {path = "../shade_runner"}
|
||||||
winit = "0.19.1"
|
winit = "0.19.1"
|
||||||
#criterion = "0.3.0"
|
#criterion = "0.3.0"
|
||||||
hprof = "0.1.3"
|
hprof = "0.1.3"
|
||||||
|
rusttype = { version = "0.7", features = ["gpu_cache"] }
|
||||||
@@ -2,12 +2,12 @@ use vulkano::command_buffer::{AutoCommandBufferBuilder, DynamicState};
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use vulkano::buffer::{BufferAccess, BufferUsage, ImmutableBuffer, CpuAccessibleBuffer};
|
use vulkano::buffer::{BufferAccess, BufferUsage, ImmutableBuffer, CpuAccessibleBuffer};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use vulkano::format::{ClearValue, Format};
|
use vulkano::format::{ClearValue, Format, R8Unorm};
|
||||||
use vulkano::framebuffer::{FramebufferAbstract, Framebuffer, RenderPass, RenderPassAbstract};
|
use vulkano::framebuffer::{FramebufferAbstract, Framebuffer, RenderPass, RenderPassAbstract};
|
||||||
use vulkano::device::{Device, Queue};
|
use vulkano::device::{Device, Queue};
|
||||||
use vulkano::instance::PhysicalDevice;
|
use vulkano::instance::PhysicalDevice;
|
||||||
use vulkano::image::immutable::ImmutableImage;
|
use vulkano::image::immutable::ImmutableImage;
|
||||||
use vulkano::image::{Dimensions, ImageAccess, ImageDimensions, SwapchainImage, ImageUsage, AttachmentImage};
|
use vulkano::image::{Dimensions, ImageAccess, ImageDimensions, SwapchainImage, ImageUsage, AttachmentImage, ImageLayout};
|
||||||
use vulkano::sampler::{Sampler, SamplerAddressMode, MipmapMode, Filter};
|
use vulkano::sampler::{Sampler, SamplerAddressMode, MipmapMode, Filter};
|
||||||
use vulkano::descriptor::DescriptorSet;
|
use vulkano::descriptor::DescriptorSet;
|
||||||
use vulkano::descriptor::descriptor_set::PersistentDescriptorSet;
|
use vulkano::descriptor::descriptor_set::PersistentDescriptorSet;
|
||||||
@@ -21,7 +21,7 @@ use vulkano::descriptor::descriptor::DescriptorDescTy::TexelBuffer;
|
|||||||
use crate::canvas_frame::CanvasFrame;
|
use crate::canvas_frame::CanvasFrame;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use crate::canvas_shader::{CanvasShader, CanvasShaderHandle};
|
use crate::canvas_shader::{CanvasShader, CanvasShaderHandle};
|
||||||
use crate::canvas_buffer::{CanvasImage, CanvasTexture};
|
use crate::canvas_buffer::{CanvasImage, CanvasTexture, CanvasText, CanvasTextCache};
|
||||||
use crate::vertex_3d::Vertex3D;
|
use crate::vertex_3d::Vertex3D;
|
||||||
|
|
||||||
/// A drawable object can be passed into a CanvasFrame to be rendered
|
/// A drawable object can be passed into a CanvasFrame to be rendered
|
||||||
@@ -83,6 +83,7 @@ pub struct CanvasState {
|
|||||||
image_buffers: Vec<Arc<CanvasImage>>,
|
image_buffers: Vec<Arc<CanvasImage>>,
|
||||||
texture_buffers: Vec<Arc<CanvasTexture>>,
|
texture_buffers: Vec<Arc<CanvasTexture>>,
|
||||||
shader_buffers: Vec<Arc<CanvasShader>>,
|
shader_buffers: Vec<Arc<CanvasShader>>,
|
||||||
|
text_buffers: Vec<Arc<(CanvasText, CanvasTextCache)>>,
|
||||||
|
|
||||||
// Hold onto the vertices we get from the Compu and Canvas Frames
|
// Hold onto the vertices we get from the Compu and Canvas Frames
|
||||||
// When the run comes around, push the vertices to the GPU
|
// When the run comes around, push the vertices to the GPU
|
||||||
@@ -183,6 +184,7 @@ impl CanvasState {
|
|||||||
texture_buffers: vec![],
|
texture_buffers: vec![],
|
||||||
shader_buffers: vec![],
|
shader_buffers: vec![],
|
||||||
|
|
||||||
|
text_buffers: vec![],
|
||||||
colored_drawables: vec![],
|
colored_drawables: vec![],
|
||||||
colored_vertex_buffer: vec![],
|
colored_vertex_buffer: vec![],
|
||||||
textured_drawables: HashMap::default(),
|
textured_drawables: HashMap::default(),
|
||||||
@@ -196,6 +198,45 @@ impl CanvasState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Using the dimensions and suggested usage, load a CanvasImage and return it's handle
|
||||||
|
pub fn create_text_buffers(&mut self, dimensions: (u32, u32)) -> Arc<CanvasTextHandle> {
|
||||||
|
|
||||||
|
let handle = Arc::new(CanvasTextHandle { handle: self.text_buffers.len() as u32 });
|
||||||
|
|
||||||
|
let text = CanvasText {
|
||||||
|
handle: handle.clone(),
|
||||||
|
buffer: 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().0,
|
||||||
|
size: dimensions,
|
||||||
|
};
|
||||||
|
|
||||||
|
let text_cache = CanvasTextCache {
|
||||||
|
handle: handle.clone(),
|
||||||
|
buffer: CpuAccessibleBuffer::<[u8]>::from_iter(
|
||||||
|
self.device.clone(),
|
||||||
|
BufferUsage::all(),
|
||||||
|
cache_pixel_buffer.iter().cloned()
|
||||||
|
).unwrap(),
|
||||||
|
size: dimensions,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.text_buffers.push(Arc::new((text, text_cache)));
|
||||||
|
|
||||||
|
handle
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Using the dimensions and suggested usage, load a CanvasImage and return it's handle
|
/// Using the dimensions and suggested usage, load a CanvasImage and return it's handle
|
||||||
pub fn create_image(&mut self, dimensions: (u32, u32), usage: ImageUsage) -> Arc<CanvasImageHandle> {
|
pub fn create_image(&mut self, dimensions: (u32, u32), usage: ImageUsage) -> Arc<CanvasImageHandle> {
|
||||||
let handle = Arc::new(CanvasImageHandle { handle: self.image_buffers.len() as u32 });
|
let handle = Arc::new(CanvasImageHandle { handle: self.image_buffers.len() as u32 });
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
use crate::canvas::{CanvasTextureHandle, CanvasImageHandle};
|
use crate::canvas::{CanvasTextureHandle, CanvasImageHandle};
|
||||||
use vulkano::image::{ImmutableImage, AttachmentImage};
|
use vulkano::image::{ImmutableImage, AttachmentImage};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use vulkano::format::Format;
|
use vulkano::format::{Format, R8Unorm};
|
||||||
use crate::canvas_shader::CanvasShader;
|
use crate::canvas_shader::CanvasShader;
|
||||||
use vulkano::sampler::Sampler;
|
use vulkano::sampler::Sampler;
|
||||||
use vulkano::descriptor::DescriptorSet;
|
use vulkano::descriptor::DescriptorSet;
|
||||||
use vulkano::descriptor::descriptor_set::PersistentDescriptorSet;
|
use vulkano::descriptor::descriptor_set::PersistentDescriptorSet;
|
||||||
|
use vulkano::buffer::CpuAccessibleBuffer;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct CanvasTexture {
|
pub struct CanvasTexture {
|
||||||
@@ -17,8 +18,8 @@ pub struct CanvasTexture {
|
|||||||
|
|
||||||
impl CanvasTexture {
|
impl CanvasTexture {
|
||||||
pub fn get_descriptor_set(&self,
|
pub fn get_descriptor_set(&self,
|
||||||
shader: Arc<CanvasShader>,
|
shader: Arc<CanvasShader>,
|
||||||
sampler: Arc<Sampler>) -> Box<dyn DescriptorSet + Send + Sync> {
|
sampler: Arc<Sampler>) -> Box<dyn DescriptorSet + Send + Sync> {
|
||||||
let o: Box<dyn DescriptorSet + Send + Sync> = Box::new(
|
let o: Box<dyn DescriptorSet + Send + Sync> = Box::new(
|
||||||
PersistentDescriptorSet::start(
|
PersistentDescriptorSet::start(
|
||||||
shader.clone().get_pipeline().clone(), 0,
|
shader.clone().get_pipeline().clone(), 0,
|
||||||
@@ -38,7 +39,7 @@ pub struct CanvasImage {
|
|||||||
|
|
||||||
impl CanvasImage {
|
impl CanvasImage {
|
||||||
pub fn get_descriptor_set(&self, shader: Arc<CanvasShader>)
|
pub fn get_descriptor_set(&self, shader: Arc<CanvasShader>)
|
||||||
-> Box<dyn DescriptorSet + Send + Sync> {
|
-> Box<dyn DescriptorSet + Send + Sync> {
|
||||||
let o: Box<dyn DescriptorSet + Send + Sync> = Box::new(
|
let o: Box<dyn DescriptorSet + Send + Sync> = Box::new(
|
||||||
PersistentDescriptorSet::start(
|
PersistentDescriptorSet::start(
|
||||||
shader.clone().get_pipeline().clone(), 0,
|
shader.clone().get_pipeline().clone(), 0,
|
||||||
@@ -47,4 +48,47 @@ impl CanvasImage {
|
|||||||
.build().unwrap());
|
.build().unwrap());
|
||||||
o
|
o
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct CanvasTextCache {
|
||||||
|
pub(crate) handle: Arc<CanvasTextCacheHandle>,
|
||||||
|
pub(crate) buffer: Arc<CpuAccessibleBuffer<[u8]>>,
|
||||||
|
pub(crate) size: (u32, u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CanvasTextCache {
|
||||||
|
pub fn get_descriptor_set(&self,
|
||||||
|
shader: Arc<CanvasShader>,
|
||||||
|
sampler: Arc<Sampler>) -> Box<dyn DescriptorSet + Send + Sync> {
|
||||||
|
let o: Box<dyn DescriptorSet + Send + Sync> = Box::new(
|
||||||
|
PersistentDescriptorSet::start(
|
||||||
|
shader.clone().get_pipeline().clone(), 0,
|
||||||
|
)
|
||||||
|
.add_sampled_image(self.buffer.clone(), sampler.clone()).unwrap()
|
||||||
|
.build().unwrap());
|
||||||
|
o
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct CanvasText {
|
||||||
|
pub(crate) handle: Arc<CanvasTextHandle>,
|
||||||
|
pub(crate) buffer: Arc<ImmutableImage<R8Unorm>>,
|
||||||
|
pub(crate) size: (u32, u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CanvasText {
|
||||||
|
pub fn get_descriptor_set(&self,
|
||||||
|
shader: Arc<CanvasShader>,
|
||||||
|
sampler: Arc<Sampler>) -> Box<dyn DescriptorSet + Send + Sync> {
|
||||||
|
let o: Box<dyn DescriptorSet + Send + Sync> = Box::new(
|
||||||
|
PersistentDescriptorSet::start(
|
||||||
|
shader.clone().get_pipeline().clone(), 0,
|
||||||
|
)
|
||||||
|
.add_sampled_image(self.buffer.clone(), sampler.clone()).unwrap()
|
||||||
|
.build().unwrap());
|
||||||
|
o
|
||||||
|
}
|
||||||
}
|
}
|
||||||
177
src/canvas_text.rs
Normal file
177
src/canvas_text.rs
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
|
||||||
|
use rusttype::{Font, PositionedGlyph, Scale, Rect, point};
|
||||||
|
use rusttype::gpu_cache::Cache;
|
||||||
|
|
||||||
|
const CACHE_WIDTH: usize = 1000;
|
||||||
|
const CACHE_HEIGHT: usize = 1000;
|
||||||
|
|
||||||
|
/// 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<Device>,
|
||||||
|
queue: Arc<Queue>,
|
||||||
|
font: Font<'static>,
|
||||||
|
cache: Cache<'static>,
|
||||||
|
cache_pixel_buffer: Vec<u8>,
|
||||||
|
texts: Vec<TextData>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CanvasText {
|
||||||
|
|
||||||
|
pub fn new() -> CanvasText {
|
||||||
|
|
||||||
|
let cache = Cache::builder().dimensions(CACHE_WIDTH as u32, CACHE_HEIGHT as u32).build();
|
||||||
|
let cache_pixel_buffer = vec!(0; CACHE_WIDTH * CACHE_HEIGHT);
|
||||||
|
|
||||||
|
|
||||||
|
let font_data = include_bytes!("DejaVuSans.ttf");
|
||||||
|
let font = Font::from_bytes(font_data as &[u8]).unwrap();
|
||||||
|
|
||||||
|
CanvasText {
|
||||||
|
device: (),
|
||||||
|
queue: (),
|
||||||
|
font: font,
|
||||||
|
cache: (),
|
||||||
|
cache_pixel_buffer: (),
|
||||||
|
texts: ()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn queue_text(&mut self,
|
||||||
|
x: f32, y: f32,
|
||||||
|
size: f32, color: [f32; 4],
|
||||||
|
text: &str) {
|
||||||
|
let glyphs: Vec<PositionedGlyph> = 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<Vertex> = 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -90,7 +90,7 @@ pub fn main() {
|
|||||||
let image_data = load_raw(String::from("funky-bird.jpg"));
|
let image_data = load_raw(String::from("funky-bird.jpg"));
|
||||||
let image_dimensions_f = ((image_data.1).0 as f32, (image_data.1).1 as f32);
|
let image_dimensions_f = ((image_data.1).0 as f32, (image_data.1).1 as f32);
|
||||||
let image_dimensions_u = image_data.1;
|
let image_dimensions_u = image_data.1;
|
||||||
let compu_sprite1 = CompuSprite::new((0.0, -0.5), (0.4, 0.4), 3, image_dimensions_f,
|
let compu_sprite1 = CompuSprite::new((0.0, -0.5), (0.4, 0.4), 0, image_dimensions_f,
|
||||||
// This swap image needs to match the size of the compute
|
// This swap image needs to match the size of the compute
|
||||||
processor.new_swap_image(image_dimensions_u));
|
processor.new_swap_image(image_dimensions_u));
|
||||||
|
|
||||||
@@ -102,8 +102,8 @@ pub fn main() {
|
|||||||
let funky_handle = processor.get_texture_handle(String::from("funky-bird.jpg")).unwrap();
|
let funky_handle = processor.get_texture_handle(String::from("funky-bird.jpg")).unwrap();
|
||||||
let sfml_handle = processor.get_texture_handle(String::from("sfml.png")).unwrap();
|
let sfml_handle = processor.get_texture_handle(String::from("sfml.png")).unwrap();
|
||||||
|
|
||||||
let funky_sprite = Sprite::new_with_texture((0.0, -0.5), (0.5, 0.5), 2, funky_handle.clone());
|
let funky_sprite = Sprite::new_with_texture((0.0, -0.5), (0.5, 0.5), 0, funky_handle.clone());
|
||||||
let sfml_sprite = Sprite::new_with_texture((0.0, -0.5), (0.5, 0.5), 1, sfml_handle.clone());
|
let sfml_sprite = Sprite::new_with_texture((0.0, -0.5), (0.5, 0.5), 0, sfml_handle.clone());
|
||||||
|
|
||||||
|
|
||||||
drop(q2);
|
drop(q2);
|
||||||
|
|||||||
Reference in New Issue
Block a user