moved to rust style file tree

This commit is contained in:
2019-09-21 23:59:43 -07:00
parent fa2c9397bf
commit 34c23eebc0
21 changed files with 146 additions and 187 deletions

View File

@@ -0,0 +1,95 @@
use crate::canvas::canvas_state::{CanvasTextureHandle, CanvasImageHandle};
use vulkano::image::{ImmutableImage, AttachmentImage};
use std::sync::Arc;
use vulkano::format::{Format, R8Unorm};
use crate::canvas::canvas_shader::CanvasShader;
use vulkano::sampler::Sampler;
use vulkano::descriptor::DescriptorSet;
use vulkano::descriptor::descriptor_set::PersistentDescriptorSet;
use vulkano::buffer::CpuAccessibleBuffer;
use crate::canvas::canvas_text::{CanvasTextCacheHandle, CanvasTextHandle};
#[derive(Clone)]
pub struct CanvasTexture {
pub(crate) handle: Arc<CanvasTextureHandle>,
pub(crate) buffer: Arc<ImmutableImage<Format>>,
pub(crate) name: String,
pub(crate) size: (u32, u32),
}
impl CanvasTexture {
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 CanvasImage {
pub(crate) handle: Arc<CanvasImageHandle>,
pub(crate) buffer: Arc<AttachmentImage>,
pub(crate) size: (u32, u32),
}
impl CanvasImage {
pub fn get_descriptor_set(&self, shader: Arc<CanvasShader>)
-> Box<dyn DescriptorSet + Send + Sync> {
let o: Box<dyn DescriptorSet + Send + Sync> = Box::new(
PersistentDescriptorSet::start(
shader.clone().get_pipeline().clone(), 0,
)
.add_image(self.buffer.clone()).unwrap()
.build().unwrap());
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_buffer(self.buffer.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
}
}

View File

@@ -0,0 +1,48 @@
use crate::util::vertex_3d::{Vertex3D};
use std::sync::Arc;
use std::collections::HashMap;
use crate::canvas::canvas_state::{Drawable, CanvasTextureHandle, CanvasImageHandle};
///
pub struct CanvasFrame {
pub colored_drawables: Vec<Vertex3D>,
pub textured_drawables: HashMap<Arc<CanvasTextureHandle>, Vec<Vec<Vertex3D>>>,
pub image_drawables: HashMap<Arc<CanvasImageHandle>, Vec<Vec<Vertex3D>>>,
}
impl CanvasFrame {
/// Creates a bare canvas frame with empty accumulators
pub fn new() -> CanvasFrame {
CanvasFrame {
colored_drawables: vec![],
textured_drawables: Default::default(),
image_drawables: Default::default(),
}
}
/// Accumulates the drawables collected Vertex2D's
pub fn draw(&mut self, drawable: &dyn Drawable) {
match drawable.get_texture_handle() {
Some(handle) => {
self.textured_drawables
.entry(handle.clone())
.or_insert(Vec::new())
.push(drawable.collect());
}
None => {
match drawable.get_image_handle() {
Some(handle) => {
self.image_drawables
.entry(handle.clone())
.or_insert(Vec::new())
.push(drawable.collect());
}
None => {
self.colored_drawables.extend(drawable.collect());
}
}
}
}
}
}

294
src/canvas/canvas_shader.rs Normal file
View File

@@ -0,0 +1,294 @@
use vulkano::device::{Device, Queue};
use vulkano::instance::{PhysicalDevice, QueueFamily};
use vulkano::pipeline::{GraphicsPipeline, GraphicsPipelineAbstract, GraphicsPipelineBuilder};
use std::sync::Arc;
use std::ffi::CStr;
use std::path::PathBuf;
use shade_runner as sr;
use vulkano::framebuffer::{Subpass, RenderPassAbstract, Framebuffer, FramebufferAbstract};
use vulkano::pipeline::shader::{GraphicsShaderType, ShaderModule, SpecializationConstants, SpecializationMapEntry};
use vulkano::swapchain::Capabilities;
use vulkano::pipeline::vertex::SingleBufferDefinition;
use crate::util::vertex_3d::Vertex3D;
use vulkano::pipeline::depth_stencil::{DepthStencil, Stencil, StencilOp, Compare, DepthBounds};
use std::collections::HashSet;
/// Typed wrapper for a u32 handle
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct CompiledGraphicsPipelineHandle {
pub handle: u32
}
pub trait CompiledGraphicsPipeline {
// fn get_paths(&self, filename: String) -> Vec<(ShaderType, PathBuf)> {
//
// let project_root =
// std::env::current_dir()
// .expect("failed to get root directory");
//
// let mut shader_path = project_root.clone();
// shader_path.push(PathBuf::from("resources/shaders/"));
//
//
//
// let mut vertex_shader_path = shader_path.clone();
// vertex_shader_path.push(PathBuf::from("resources/shaders/"));
// vertex_shader_path.push(PathBuf::from(filename.clone() + ".vertex"));
//
// let mut fragment_shader_path = project_root.clone();
// fragment_shader_path.push(PathBuf::from("resources/shaders/"));
// fragment_shader_path.push(PathBuf::from(filename.clone() + ".fragment"));
//
// (vertex_shader_path, fragment_shader_path)
// }
fn new(filename: String, shaders: HashSet<ShaderType>) -> Self;
fn get_name() -> String;
fn get_handle() -> Arc<CompiledGraphicsPipelineHandle>;
fn get_pipeline() -> Arc<dyn GraphicsPipelineAbstract + Sync + Send>;
fn recompile();
}
/// Legacy ShaderType enum for single type shaders.
#[derive(PartialEq, Eq, Hash, Clone)]
pub enum ShaderType {
VERTEX = 0,
FRAGMENT = 1,
GEOMETRY = 2,
TESSELATION = 3,
}
/// Typed wrapper for a u32 shader handle (index id)
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct CanvasShaderHandle {
pub handle: u32
}
/// CanvasShader holds the pipeline and render pass for the input shader source
#[derive(Clone)]
pub struct CanvasShader {
graphics_pipeline: Option<Arc<dyn GraphicsPipelineAbstract + Sync + Send>>,
device: Arc<Device>,
pub(crate) handle: Arc<CanvasShaderHandle>,
pub(crate) name: String,
}
impl CompiledGraphicsPipeline for CanvasShader {
// fn get_path(filename: String) -> (PathBuf, PathBuf) {
// let project_root =
// std::env::current_dir()
// .expect("failed to get root directory");
//
// let mut shader_path = project_root.clone();
// shader_path.push(PathBuf::from("resources/shaders/"));
//
// let mut vertex_shader_path = project_root.clone();
// vertex_shader_path.push(PathBuf::from("resources/shaders/"));
// vertex_shader_path.push(PathBuf::from(filename.clone() + ".vertex"));
//
// let mut fragment_shader_path = project_root.clone();
// fragment_shader_path.push(PathBuf::from("resources/shaders/"));
// fragment_shader_path.push(PathBuf::from(filename.clone() + ".fragment"));
//
// (vertex_shader_path, fragment_shader_path)
// }
fn new(filename: String, shaders: HashSet<ShaderType>) -> Self {
unimplemented!()
}
fn get_name() -> String {
unimplemented!()
}
fn get_handle() -> Arc<CompiledGraphicsPipelineHandle> {
unimplemented!()
}
fn get_pipeline() -> Arc<dyn GraphicsPipelineAbstract + Sync + Send> {
unimplemented!()
}
fn recompile() {
unimplemented!()
}
}
impl CanvasShader {
fn get_path(filename: String) -> (PathBuf, PathBuf) {
let project_root =
std::env::current_dir()
.expect("failed to get root directory");
let mut shader_path = project_root.clone();
shader_path.push(PathBuf::from("resources/shaders/"));
let mut vertex_shader_path = project_root.clone();
vertex_shader_path.push(PathBuf::from("resources/shaders/"));
vertex_shader_path.push(PathBuf::from(filename.clone() + ".vertex"));
let mut fragment_shader_path = project_root.clone();
fragment_shader_path.push(PathBuf::from("resources/shaders/"));
fragment_shader_path.push(PathBuf::from(filename.clone() + ".fragment"));
(vertex_shader_path, fragment_shader_path)
}
/// Clone and returns the compiled graphics pipeline
pub fn get_pipeline(&self) -> Arc<dyn GraphicsPipelineAbstract + Sync + Send> {
self.graphics_pipeline.clone().unwrap()
}
/// Create a new shader.
/// This will explode when the shader does not want to compile
pub fn new(filename: String,
capabilities: Capabilities,
queue: Arc<Queue>,
physical: PhysicalDevice,
device: Arc<Device>,
handle: Arc<CanvasShaderHandle>,
render_pass: Arc<dyn RenderPassAbstract + Send + Sync>,) -> CanvasShader {
let format = capabilities.supported_formats[0].0;
let filenames = CanvasShader::get_path(filename.clone());
// TODO: better compile message, run til successful compile
let shader = sr::load(filenames.0, filenames.1)
.expect("Shader didn't compile");
let vulkano_entry =
sr::parse(&shader)
.expect("failed to parse");
let fragment_shader_module: Arc<ShaderModule> = unsafe {
let filenames1 = CanvasShader::get_path(filename.clone());
let shader1 = sr::load(filenames1.0, filenames1.1)
.expect("Shader didn't compile");
vulkano::pipeline::shader::ShaderModule::from_words(device.clone(), &shader1.fragment.clone())
}.unwrap();
let vertex_shader_module: Arc<ShaderModule> = unsafe {
let filenames1 = CanvasShader::get_path(filename.clone());
let shader1 = sr::load(filenames1.0, filenames1.1)
.expect("Shader didn't compile");
vulkano::pipeline::shader::ShaderModule::from_words(device.clone(), &shader1.vertex.clone())
}.unwrap();
let filenames = CanvasShader::get_path(filename.clone());
let frag_entry_point = unsafe {
Some(fragment_shader_module.graphics_entry_point(CStr::from_bytes_with_nul_unchecked(b"main\0"),
vulkano_entry.frag_input,
vulkano_entry.frag_output,
vulkano_entry.frag_layout,
GraphicsShaderType::Fragment))
};
let vertex_entry_point = unsafe {
Some(vertex_shader_module.graphics_entry_point(CStr::from_bytes_with_nul_unchecked(b"main\0"),
vulkano_entry.vert_input,
vulkano_entry.vert_output,
vulkano_entry.vert_layout,
GraphicsShaderType::Vertex))
};
let stencil = DepthStencil {
depth_compare: Compare::Less,
depth_write: true,
depth_bounds_test: DepthBounds::Disabled,
stencil_front: Stencil {
compare: Compare::Equal,
pass_op: StencilOp::IncrementAndWrap,
fail_op: StencilOp::DecrementAndClamp,
depth_fail_op: StencilOp::Keep,
compare_mask: None,
write_mask: None,
reference: None,
},
stencil_back: Stencil {
compare: Compare::Equal,
pass_op: StencilOp::Invert,
fail_op: StencilOp::Zero,
depth_fail_op: StencilOp::Zero,
compare_mask: None,
write_mask: None,
reference: None,
},
};
CanvasShader {
graphics_pipeline: Some(Arc::new(GraphicsPipeline::start()
.vertex_input(SingleBufferDefinition::<Vertex3D>::new())
.vertex_shader(vertex_entry_point.clone().unwrap(), ShaderSpecializationConstants {
first_constant: 0,
second_constant: 0,
third_constant: 0.0,
})
.triangle_list()
// Use a resizable viewport set to draw over the entire window
.viewports_dynamic_scissors_irrelevant(1)
.fragment_shader(frag_entry_point.clone().unwrap(), ShaderSpecializationConstants {
first_constant: 0,
second_constant: 0,
third_constant: 0.0,
})
.depth_stencil(stencil)
// We have to indicate which subpass of which render pass this pipeline is going to be used
// in. The pipeline will only be usable from this particular subpass.
.render_pass(Subpass::from(render_pass.clone(), 0).unwrap())
.build(device.clone())
.unwrap())),
device: device,
handle: handle.clone(),
name: filename.clone(),
}
}
}
#[repr(C)]
#[derive(Default, Debug, Clone)]
/// Specialization constants which can be passed to the shader. Pretty much placeholder ATM
struct ShaderSpecializationConstants {
first_constant: i32,
second_constant: u32,
third_constant: f32,
}
unsafe impl SpecializationConstants for ShaderSpecializationConstants {
fn descriptors() -> &'static [SpecializationMapEntry] {
static DESCRIPTORS: [SpecializationMapEntry; 3] = [
SpecializationMapEntry {
constant_id: 0,
offset: 0,
size: 4,
},
SpecializationMapEntry {
constant_id: 1,
offset: 4,
size: 4,
},
SpecializationMapEntry {
constant_id: 2,
offset: 8,
size: 4,
},
];
&DESCRIPTORS
}
}

617
src/canvas/canvas_state.rs Normal file
View File

@@ -0,0 +1,617 @@
use vulkano::command_buffer::{AutoCommandBufferBuilder, DynamicState};
use std::collections::HashMap;
use vulkano::buffer::{BufferAccess, BufferUsage, ImmutableBuffer, CpuAccessibleBuffer};
use std::sync::Arc;
use vulkano::format::{ClearValue, Format, R8Unorm};
use vulkano::framebuffer::{FramebufferAbstract, Framebuffer, RenderPass, RenderPassAbstract};
use vulkano::device::{Device, Queue};
use vulkano::instance::PhysicalDevice;
use vulkano::image::immutable::ImmutableImage;
use vulkano::image::{Dimensions, ImageAccess, ImageDimensions, SwapchainImage, ImageUsage, AttachmentImage, ImageLayout};
use vulkano::sampler::{Sampler, SamplerAddressMode, MipmapMode, Filter};
use vulkano::descriptor::DescriptorSet;
use vulkano::descriptor::descriptor_set::PersistentDescriptorSet;
use std::path::PathBuf;
use image::GenericImageView;
use std::iter::FromIterator;
use vulkano::swapchain::Capabilities;
use winit::Window;
use vulkano::pipeline::viewport::Viewport;
use vulkano::descriptor::descriptor::DescriptorDescTy::TexelBuffer;
use crate::canvas::canvas_frame::CanvasFrame;
use std::hash::Hash;
use crate::canvas::canvas_text::{CanvasText, CanvasTextHandle};
use crate::canvas::canvas_shader::{CanvasShader, CanvasShaderHandle};
use crate::canvas::canvas_buffer::{CanvasImage, CanvasTexture, CanvasTextCache};
use crate::util::vertex_3d::Vertex3D;
use vulkano::pipeline::depth_stencil::{StencilFaceFlags, DynamicStencilValue};
/// A drawable object can be passed into a CanvasFrame to be rendered
/// Very generic implementation. (N % 2 == 0) vertices, ditto for texture coords, and rgba color
/// Provides Image and Texture handles for drawing
/// Split out to two drawables?
///
pub trait Drawable {
fn get_vertices(&self) -> Vec<(f32, f32, f32)>;
fn get_color(&self) -> (f32, f32, f32, f32);
fn get_ti_coords(&self) -> Vec<(f32, f32)>;
fn get_texture_handle(&self) -> Option<Arc<CanvasTextureHandle>>;
fn get_image_handle(&self) -> Option<Arc<CanvasImageHandle>>;
// fn get_text_handle(&self) -> Option<Arc<CanvasTextHandle>>;
fn collect(&self) -> Vec<Vertex3D> {
let color = self.get_color();
self.get_vertices().iter().zip(self.get_ti_coords().iter()).map(|(a, b)|
Vertex3D {
v_position: [a.0, a.1, a.2],
color: [color.0, color.1, color.2, color.3],
ti_position: [b.0, b.1],
}).collect()
}
}
/// Legacy ShaderType enum for single type shaders.
#[derive(PartialEq, Eq, Hash, Clone)]
pub enum ShaderType {
SOLID = 0,
TEXTURED = 1,
IMAGE = 2,
}
/// Typed wrapper for a u32 texture handle (index id)
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct CanvasTextureHandle {
pub handle: u32
}
/// Typed wrapper for a u32 image handle (index id)
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct CanvasImageHandle {
pub handle: u32
}
/// Canvas state is used for storage of texture and image buffers in addition to vertex buffers
/// Canvas state also contains logic for writing the stored buffers to the command_buffer
#[derive(Clone)]
pub struct CanvasState {
/// Generated during new()
dynamic_state: DynamicState,
/// Generated during new()
sampler: Arc<Sampler>,
// hold the image, texture, and shader buffers the same was as we do CompuState
image_buffers: Vec<Arc<CanvasImage>>,
texture_buffers: Vec<Arc<CanvasTexture>>,
shader_buffers: Vec<Arc<CanvasShader>>,
text_buffers: Vec<Arc<(CanvasText, CanvasTextCache)>>,
// Hold onto the vertices we get from the Compu and Canvas Frames
// When the run comes around, push the vertices to the GPU
colored_drawables: Vec<Vertex3D>,
colored_vertex_buffer: Vec<Arc<(dyn BufferAccess + std::marker::Send + std::marker::Sync)>>,
textured_drawables: HashMap<Arc<CanvasTextureHandle>, Vec<Vec<Vertex3D>>>,
textured_vertex_buffer: HashMap<Arc<CanvasTextureHandle>, Arc<(dyn BufferAccess + std::marker::Send + std::marker::Sync)>>,
image_drawables: HashMap<Arc<CanvasImageHandle>, Vec<Vec<Vertex3D>>>,
image_vertex_buffer: HashMap<Arc<CanvasImageHandle>, Arc<(dyn BufferAccess + std::marker::Send + std::marker::Sync)>>,
// Looks like we gotta hold onto the queue for managing textures
queue: Arc<Queue>,
device: Arc<Device>,
render_pass: Arc<dyn RenderPassAbstract + Send + Sync>,
}
impl CanvasState {
/// This method is called once during initialization, then again whenever the window is resized
pub fn window_size_dependent_setup(&mut self, images: &[Arc<SwapchainImage<Window>>])
-> Vec<Arc<dyn FramebufferAbstract + Send + Sync>> {
let dimensions = images[0].dimensions();
self.dynamic_state.viewports =
Some(vec![Viewport {
origin: [0.0, 0.0],
dimensions: [dimensions.width() as f32, dimensions.height() as f32],
depth_range: 0.0..1.0,
}]);
let dimensions = [dimensions.width(), dimensions.height()];
let depth_buffer = AttachmentImage::transient(self.device.clone(), dimensions, Format::D32Sfloat_S8Uint).unwrap();
images.iter().map(|image| {
Arc::new(
Framebuffer::start(self.render_pass.clone())
.add(image.clone()).unwrap()
.add(depth_buffer.clone()).unwrap()
.build().unwrap()
) as Arc<dyn FramebufferAbstract + Send + Sync>
}).collect::<Vec<_>>()
}
/// Creates a Canvas State. Which at this point is pretty empty
pub fn new(queue: Arc<Queue>,
device: Arc<Device>,
physical: PhysicalDevice,
capabilities: Capabilities) -> CanvasState {
let format = capabilities.supported_formats[0].0;
let render_pass = Arc::new(vulkano::single_pass_renderpass!(
device.clone(),
// Attachments are outgoing like f_color
attachments: {
// `color` is a custom name we give to the first and only attachment.
color: {
// `load: Clear` means that we ask the GPU to clear the content of this
// attachment at the start of the drawing.
load: Clear,
// `store: Store` means that we ask the GPU to store the output of the draw
// in the actual image. We could also ask it to discard the result.
store: Store,
// `format: <ty>` indicates the type of the format of the image. This has to
// be one of the types of the `vulkano::format` module (or alternatively one
// of your structs that implements the `FormatDesc` trait). Here we use the
// same format as the swapchain.
format: format,
samples: 1,
},
depth: {
load: Clear,
store: DontCare,
format: Format::D32Sfloat_S8Uint,
samples: 1,
}
},
pass: {
// We use the attachment named `color` as the one and only color attachment.
color: [color],
// No depth-stencil attachment is indicated with empty brackets.
depth_stencil: {depth}
}
).unwrap());
CanvasState {
dynamic_state: DynamicState {
line_width: None,
viewports: None,
scissors: None,
compare_mask: Some(DynamicStencilValue {
face: StencilFaceFlags::StencilFrontAndBack,
value: 0xFF,
}),
write_mask: Some(DynamicStencilValue {
face: StencilFaceFlags::StencilFrontAndBack,
value: 0xFF,
}),
reference: Some(DynamicStencilValue {
face: StencilFaceFlags::StencilFrontAndBack,
value: 0xFF,
}),
},
sampler: Sampler::new(device.clone(),
Filter::Linear, Filter::Linear,
MipmapMode::Nearest,
SamplerAddressMode::Repeat, SamplerAddressMode::Repeat,
SamplerAddressMode::Repeat, 0.0, 1.0, 0.0, 0.0).unwrap(),
image_buffers: vec![],
texture_buffers: vec![],
shader_buffers: vec![],
text_buffers: vec![],
colored_drawables: vec![],
colored_vertex_buffer: vec![],
textured_drawables: HashMap::default(),
textured_vertex_buffer: Default::default(),
image_drawables: Default::default(),
image_vertex_buffer: Default::default(),
queue: queue.clone(),
device: device.clone(),
render_pass: render_pass.clone(),
}
}
/// 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
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 image = CanvasImage {
handle: handle.clone(),
buffer: AttachmentImage::with_usage(
self.device.clone(),
[dimensions.0, dimensions.1],
Format::R8G8B8A8Uint,
usage).unwrap(),
size: dimensions,
};
self.image_buffers.push(Arc::new(image));
handle
}
/// Return the image buffer from an input image handle
pub fn get_image(&self, image_handle: Arc<CanvasImageHandle>) -> Arc<AttachmentImage> {
self.image_buffers.get((*image_handle).clone().handle as usize).unwrap()
.clone().buffer.clone()
}
/// Load a texture buffer from an input filename
fn get_texture_from_file(&self, image_filename: String) -> Arc<ImmutableImage<Format>> {
let project_root =
std::env::current_dir()
.expect("failed to get root directory");
let mut compute_path = project_root.clone();
compute_path.push(PathBuf::from("resources/images/"));
compute_path.push(PathBuf::from(image_filename));
let img = image::open(compute_path).expect("Couldn't find image");
let xy = img.dimensions();
let data_length = xy.0 * xy.1 * 4;
let pixel_count = img.raw_pixels().len();
let mut image_buffer = Vec::new();
if pixel_count != data_length as usize {
println!("Creating apha channel...");
for i in img.raw_pixels().iter() {
if (image_buffer.len() + 1) % 4 == 0 {
image_buffer.push(255);
}
image_buffer.push(*i);
}
image_buffer.push(255);
} else {
image_buffer = img.raw_pixels();
}
let (texture, tex_future) = ImmutableImage::from_iter(
image_buffer.iter().cloned(),
Dimensions::Dim2d { width: xy.0, height: xy.1 },
Format::R8G8B8A8Srgb,
self.queue.clone(),
).unwrap();
texture
}
/// Load a texture using it's filename from a file. Returns the handle of the loaded texture
pub fn load_texture(&mut self, filename: String) -> Option<Arc<CanvasTextureHandle>> {
let texture_buffer = self.get_texture_from_file(filename.clone());
let handle = Arc::new(CanvasTextureHandle {
handle: self.texture_buffers.len() as u32
});
let texture = Arc::new(CanvasTexture {
handle: handle.clone(),
buffer: self.get_texture_from_file(filename.clone()),
name: filename.clone(),
size: (0, 0),
});
self.texture_buffers.push(texture);
Some(handle)
}
/// Load and Compile a shader with the filename at resources/shaders
/// Takes physical and capabilities as we don't store that in Canvas
pub fn load_shader(&mut self,
filename: String,
shader_type: ShaderType,
physical: PhysicalDevice,
capabilities: Capabilities) -> Option<Arc<CanvasShaderHandle>> {
let handle = Arc::new(CanvasShaderHandle {
handle: self.shader_buffers.len() as u32
});
// TODO : what is even going on here
let shader = match shader_type {
ShaderType::SOLID => {
Arc::new(CanvasShader::new(
filename.clone(),
capabilities.clone(),
self.queue.clone(),
physical.clone(),
self.device.clone(),
handle.clone(),
self.render_pass.clone())
)
}
ShaderType::IMAGE | ShaderType::TEXTURED => {
Arc::new(CanvasShader::new(
filename.clone(),
capabilities.clone(),
self.queue.clone(),
physical.clone(),
self.device.clone(),
handle.clone(),
self.render_pass.clone())
)
}
};
self.shader_buffers.push(shader.clone());
Some(handle)
}
/// Using the texture name, iterates through the stored textures and matches by the name
pub fn get_texture_handle(&self, texture_name: String)
-> Option<Arc<CanvasTextureHandle>> {
for i in self.texture_buffers.clone() {
if i.name == texture_name {
return Some(i.handle.clone());
}
}
None
}
/// Using the shader name, iterates through the stored textures and matches by the name
pub fn get_shader_handle(&self, shader_name: String)
-> Option<Arc<CanvasShaderHandle>> {
for shader in self.shader_buffers.clone() {
if shader.name == shader_name {
return Some(shader.handle.clone());
}
}
None
}
/// Using the texture handle, grab the stored texture and return the buffer
pub fn get_texture(&self, texture_handle: Arc<CanvasTextureHandle>)
-> Arc<ImmutableImage<Format>> {
let handle = texture_handle.handle as usize;
if let Some(i) = self.texture_buffers.get(handle) {
return i.clone().buffer.clone();
} else {
panic!("{} : Texture not loaded", handle);
}
}
/// Scrape all the values from the CanvasFrame and then allocate the vertex buffers
pub fn draw(&mut self, canvas_frame: CanvasFrame) {
self.textured_drawables = canvas_frame.textured_drawables;
self.colored_drawables = canvas_frame.colored_drawables;
self.image_drawables = canvas_frame.image_drawables;
self.allocate_vertex_buffers();
}
/// draw(canvas_fame) stored all the intermediate information, this function
/// allocates the vertex buffers using that information
fn allocate_vertex_buffers(&mut self) {
self.colored_vertex_buffer.clear();
{
let g = hprof::enter("Colored Vertex Buffer");
self.colored_vertex_buffer.push(
ImmutableBuffer::from_iter(
self.colored_drawables.iter().cloned(),
BufferUsage::vertex_buffer(),
self.queue.clone(),
).unwrap().0
);
}
self.textured_vertex_buffer.clear();
{
let g = hprof::enter("Textured Vertex Buffer");
for (k, v) in self.textured_drawables.drain() {
let vertex_buffer = v.clone().iter()
.fold(Vec::new(), |mut a: Vec<Vertex3D>, b| {
a.extend(b);
a
});
self.textured_vertex_buffer.insert(
k.clone(),
ImmutableBuffer::from_iter(
vertex_buffer.iter().cloned(),
BufferUsage::vertex_buffer(),
self.queue.clone(),
).unwrap().0,
);
}
}
self.image_vertex_buffer.clear();
{
let g = hprof::enter("Image Vertex Buffer");
for (k, v) in self.image_drawables.drain() {
let vertex_buffer = v.clone().iter()
.fold(Vec::new(), |mut a: Vec<Vertex3D>, b| {
a.extend(b);
a
});
self.image_vertex_buffer.insert(
k.clone(),
ImmutableBuffer::from_iter(
vertex_buffer.iter().cloned(),
BufferUsage::vertex_buffer(),
self.queue.clone(),
).unwrap().0,
);
}
}
}
/// Builds the descriptor set for solid colors using the input kernel (needs to support solid colors)
fn get_solid_color_descriptor_set(&self, kernel: Arc<CanvasShader>) -> Box<dyn DescriptorSet + Send + Sync> {
let o: Box<dyn DescriptorSet + Send + Sync> = Box::new(
PersistentDescriptorSet::start(
kernel.clone().get_pipeline().clone(), 0,
).build().unwrap());
o
}
/// Pushes the draw commands to the command buffer. Requires the framebuffers and
/// image number to be passed in as they are taken care of by the vkprocessor
pub fn draw_commands(&mut self,
mut command_buffer: AutoCommandBufferBuilder,
framebuffers: Vec<Arc<dyn FramebufferAbstract + Send + Sync>>,
image_num: usize) -> AutoCommandBufferBuilder {
// Specify the color to clear the framebuffer with i.e. blue
let clear_values = vec!(
ClearValue::Float([0.0, 0.0, 1.0, 1.0]),
ClearValue::DepthStencil((1.0, 0x00)),
);
// self.dynamic_state = DynamicState {
// line_width: None,
// viewports: self.dynamic_state.viewports.clone(),
// scissors: None,
// compare_mask: Some(StencilMask{ face: StencilFaceFlags::StencilFaceFrontBit, mask: 0xFF }),
// };
let mut command_buffer = command_buffer.begin_render_pass(
framebuffers[image_num].clone(), false, clear_values.clone(),
).unwrap();
// Solid colors
let mut shader = self.shader_buffers.get(
self.get_shader_handle(String::from("color-passthrough"))
.unwrap().clone().handle as usize
).unwrap();
// This looks a little weird as colored_vertex_buffer is a vec of GPU allocated vecs.
// But we can pass in multiple vertex buffers
if !self.colored_vertex_buffer.is_empty() {
command_buffer = command_buffer.draw(
shader.get_pipeline().clone(),
&self.dynamic_state.clone(),
self.colored_vertex_buffer.clone(),
(), (),
).unwrap();
}
// Images
let mut shader = self.shader_buffers.get(
self.get_shader_handle(String::from("simple_image"))
.unwrap().clone().handle as usize
).unwrap();
if !self.image_vertex_buffer.is_empty() {
for (image_handle, vertex_buffer) in self.image_vertex_buffer.clone() {
let handle = image_handle.clone().handle as usize;
let descriptor_set = self.image_buffers.get(handle).clone().unwrap().clone()
.get_descriptor_set(shader.clone());
command_buffer = command_buffer.draw(
shader.get_pipeline().clone(),
// Multiple vertex buffers must have their definition in the pipeline!
&self.dynamic_state.clone(), vec![vertex_buffer],
vec![descriptor_set], (),
).unwrap();
}
}
// Textures
let mut shader = self.shader_buffers.get(
self.get_shader_handle(String::from("simple_texture"))
.unwrap().clone().handle as usize
).unwrap();
if !self.textured_vertex_buffer.is_empty() {
for (texture_handle, vertex_buffer) in self.textured_vertex_buffer.clone() {
let handle = texture_handle.clone().handle as usize;
let descriptor_set = self.texture_buffers.get(handle).clone().unwrap().clone()
.get_descriptor_set(shader.clone(), self.sampler.clone());
command_buffer = command_buffer.draw(
shader.get_pipeline().clone(),
// Multiple vertex buffers must have their definition in the pipeline!
&self.dynamic_state.clone(), vec![vertex_buffer],
vec![descriptor_set], (),
).unwrap();
}
}
command_buffer
.end_render_pass()
.unwrap()
}
}

195
src/canvas/canvas_text.rs Normal file
View File

@@ -0,0 +1,195 @@
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;
const CACHE_WIDTH: usize = 1000;
const CACHE_HEIGHT: usize = 1000;
/// Typed wrapper for a u32 shader handle (index id)
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct CanvasTextHandle {
pub handle: u32
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct CanvasTextCacheHandle {
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<Device>,
queue: Arc<Queue>,
font: Font<'static>,
cache: Cache<'static>,
cache_pixel_buffer: Vec<u8>,
texts: Vec<u8>,
}
impl CanvasText {
pub fn new(device: Arc<Device>, queue: Arc<Queue>) -> 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!("../../resources/fonts/sansation.ttf");
let font = Font::from_bytes(font_data as &[u8]).unwrap();
CanvasText {
device: device.clone(),
queue: queue.clone(),
font: font,
cache: cache,
cache_pixel_buffer: vec![],
texts: vec![]
}
}
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<Vertex2D> = 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()
}
}

9
src/canvas/mod.rs Normal file
View File

@@ -0,0 +1,9 @@
pub mod canvas_state;
pub mod canvas_frame;
pub mod canvas_shader;
pub mod canvas_text;
pub mod canvas_buffer;
use crate::canvas::canvas_frame::CanvasFrame;