extern crate conrod_core;
#[macro_use]
extern crate vulkano;
use std::error::Error as StdError;
use std::fmt;
use std::sync::Arc;
use conrod_core::mesh::{self, Mesh};
use conrod_core::text::rt;
use conrod_core::{image, render, Rect, Scalar};
use std::ffi::CString;
use vulkano::buffer::{BufferUsage, CpuBufferPool, ImmutableBuffer};
use vulkano::descriptor_set::layout::{
DescriptorDesc, DescriptorDescImage, DescriptorDescTy, DescriptorSetDesc, DescriptorSetLayout,
};
use vulkano::descriptor_set::{DescriptorSet, DescriptorSetError, SingleLayoutDescSetPool};
use vulkano::device::physical::QueueFamily;
use vulkano::device::{Device, Queue};
use vulkano::format::Format;
use vulkano::image::view::{ImageView, ImageViewType};
use vulkano::image::{
ImageCreateFlags, ImageCreationError, ImageDimensions, ImageUsage, ImmutableImage, StorageImage,
};
use vulkano::memory::DeviceMemoryAllocError;
use vulkano::pipeline::layout::PipelineLayout;
use vulkano::pipeline::shader::{
GraphicsShaderType, ShaderInterface, ShaderInterfaceEntry, ShaderModule, ShaderStages,
SpecializationConstants,
};
use vulkano::pipeline::viewport::{Scissor, Viewport};
use vulkano::pipeline::{GraphicsPipeline, GraphicsPipelineCreationError};
use vulkano::render_pass::Subpass;
use vulkano::sampler::{Filter, MipmapMode, Sampler, SamplerAddressMode, SamplerCreationError};
use vulkano::sync::GpuFuture;
use vulkano::OomError;
pub struct Image {
pub image_access: Arc<ImmutableImage>,
pub width: u32,
pub height: u32,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)]
pub struct Vertex {
pub position: [f32; 2],
pub tex_coords: [f32; 2],
pub rgba: [f32; 4],
pub mode: u32,
}
impl_vertex!(Vertex, position, tex_coords, rgba, mode);
fn create_shader_interface_vs_in() -> ShaderInterface {
unsafe {
ShaderInterface::new_unchecked(vec![
ShaderInterfaceEntry {
location: 0..1,
format: Format::R32G32_SFLOAT,
name: Some(std::borrow::Cow::Borrowed("position")),
},
ShaderInterfaceEntry {
location: 1..2,
format: Format::R32G32_SFLOAT,
name: Some(std::borrow::Cow::Borrowed("tex_coords")),
},
ShaderInterfaceEntry {
location: 2..3,
format: Format::R32G32B32A32_SFLOAT,
name: Some(std::borrow::Cow::Borrowed("rgba")),
},
ShaderInterfaceEntry {
location: 3..4,
format: Format::R32_UINT,
name: Some(std::borrow::Cow::Borrowed("mode")),
},
])
}
}
fn create_shader_interface_vs_out() -> ShaderInterface {
unsafe {
ShaderInterface::new_unchecked(vec![
ShaderInterfaceEntry {
location: 0..1,
format: Format::R32G32_SFLOAT,
name: Some(std::borrow::Cow::Borrowed("v_Uv")),
},
ShaderInterfaceEntry {
location: 1..2,
format: Format::R32G32B32A32_SFLOAT,
name: Some(std::borrow::Cow::Borrowed("v_Color")),
},
ShaderInterfaceEntry {
location: 2..3,
format: Format::R32_UINT,
name: Some(std::borrow::Cow::Borrowed("v_Mode")),
},
])
}
}
fn create_shader_interface_fs_out() -> ShaderInterface {
unsafe {
ShaderInterface::new_unchecked(vec![ShaderInterfaceEntry {
location: 0..1,
format: Format::R32G32B32A32_SFLOAT,
name: Some(std::borrow::Cow::Borrowed("Target0")),
}])
}
}
pub struct Renderer {
pipeline: Arc<GraphicsPipeline>,
glyph_uploads: Arc<CpuBufferPool<u8>>,
glyph_cache_tex: Arc<StorageImage>,
sampler: Arc<Sampler>,
tex_descs: SingleLayoutDescSetPool,
mesh: Mesh,
}
pub struct GlyphCacheCommand<'a> {
pub glyph_cache_pixel_buffer: &'a [u8],
pub glyph_cpu_buffer_pool: Arc<CpuBufferPool<u8>>,
pub glyph_cache_texture: Arc<StorageImage>,
}
pub struct DrawCommand {
pub graphics_pipeline: Arc<GraphicsPipeline>,
pub scissor: Scissor,
pub viewport: Viewport,
pub descriptor_set: Arc<dyn DescriptorSet + Send + Sync>,
pub vertex_buffer: Arc<ImmutableBuffer<[Vertex]>>,
}
#[derive(Debug)]
pub enum RendererCreationError {
SamplerCreation(SamplerCreationError),
ShaderLoad(vulkano::OomError),
GraphicsPipelineCreation(GraphicsPipelineCreationError),
ImageCreation(ImageCreationError),
}
#[derive(Debug)]
pub enum DrawError {
DescriptorSet(DescriptorSetError),
VertexBufferAlloc(DeviceMemoryAllocError),
}
impl mesh::ImageDimensions for Image {
fn dimensions(&self) -> [u32; 2] {
[self.width, self.height]
}
}
impl Renderer {
pub fn new(
device: Arc<Device>,
subpass: Subpass,
graphics_queue_family: QueueFamily,
window_dims: [u32; 2],
dpi_factor: f64,
) -> Result<Self, RendererCreationError> {
let [w, h] = window_dims;
let glyph_cache_dims = [
(w as f64 * dpi_factor) as u32,
(h as f64 * dpi_factor) as u32,
];
Self::with_glyph_cache_dimensions(device, subpass, graphics_queue_family, glyph_cache_dims)
}
pub fn with_glyph_cache_dimensions(
device: Arc<Device>,
subpass: Subpass,
graphics_queue_family: QueueFamily,
glyph_cache_dims: [u32; 2],
) -> Result<Self, RendererCreationError> {
let sampler = Sampler::new(
device.clone(),
Filter::Linear,
Filter::Linear,
MipmapMode::Nearest,
SamplerAddressMode::ClampToEdge,
SamplerAddressMode::ClampToEdge,
SamplerAddressMode::ClampToEdge,
0.0,
1.0,
0.0,
0.0,
)?;
let descriptor_set_desc = DescriptorSetDesc::new(vec![Some(DescriptorDesc {
ty: DescriptorDescTy::CombinedImageSampler {
image_desc: DescriptorDescImage {
format: None,
multisampled: false,
view_type: ImageViewType::Dim2d,
},
immutable_samplers: vec![],
},
stages: ShaderStages {
vertex: false,
tessellation_control: false,
tessellation_evaluation: false,
geometry: false,
fragment: true,
compute: false,
},
variable_count: false,
descriptor_count: 1,
mutable: false,
})]);
let descriptor_set_layout = Arc::new(
DescriptorSetLayout::new(device.clone(), descriptor_set_desc.clone()).unwrap(),
);
let layout = Arc::new(
PipelineLayout::new(device.clone(), vec![descriptor_set_layout], vec![]).unwrap(),
);
let vs_module =
unsafe { ShaderModule::new(device.clone(), include_bytes!("shaders/vert.spv")) }?;
let fs_module =
unsafe { ShaderModule::new(device.clone(), include_bytes!("shaders/frag.spv")) }?;
let main = CString::new("main").unwrap();
let vs_in = create_shader_interface_vs_in();
let vs_out = create_shader_interface_vs_out();
let fs_out = create_shader_interface_fs_out();
let vs = unsafe {
vs_module.graphics_entry_point(
&main,
vec![descriptor_set_desc.clone()],
None,
<()>::descriptors(),
vs_in,
vs_out.clone(),
GraphicsShaderType::Vertex,
)
};
let fs = unsafe {
fs_module.graphics_entry_point(
&main,
vec![descriptor_set_desc.clone()],
None,
<()>::descriptors(),
vs_out,
fs_out,
GraphicsShaderType::Fragment,
)
};
let pipeline = Arc::new(
GraphicsPipeline::start()
.vertex_input_single_buffer::<Vertex>()
.vertex_shader(vs, ())
.depth_stencil_disabled()
.triangle_list()
.front_face_clockwise()
.viewports_scissors_dynamic(1)
.fragment_shader(fs, ())
.blend_alpha_blending()
.render_pass(subpass)
.with_pipeline_layout(device.clone(), layout)?,
);
let mesh = Mesh::with_glyph_cache_dimensions(glyph_cache_dims);
let glyph_cache_tex = {
let [width, height] = glyph_cache_dims;
StorageImage::with_usage(
device.clone(),
ImageDimensions::Dim2d {
width,
height,
array_layers: 1,
},
Format::R8_UNORM,
ImageUsage {
transfer_destination: true,
sampled: true,
..ImageUsage::none()
},
ImageCreateFlags {
sparse_binding: false,
sparse_residency: false,
sparse_aliased: false,
mutable_format: false,
cube_compatible: false,
array_2d_compatible: false,
block_texel_view_compatible: false,
},
vec![graphics_queue_family],
)?
};
let tex_descs =
SingleLayoutDescSetPool::new(pipeline.layout().descriptor_set_layouts()[0].clone());
let glyph_uploads = Arc::new(CpuBufferPool::upload(device));
Ok(Renderer {
pipeline,
glyph_uploads,
glyph_cache_tex,
sampler,
tex_descs,
mesh,
})
}
pub fn commands(&self) -> mesh::Commands {
self.mesh.commands()
}
pub fn fill<P: render::PrimitiveWalker>(
&mut self,
image_map: &image::Map<Image>,
viewport: [f32; 4],
dpi_factor: f64,
primitives: P,
) -> Result<Option<GlyphCacheCommand>, rt::gpu_cache::CacheWriteErr> {
let Renderer {
ref glyph_uploads,
ref glyph_cache_tex,
ref mut mesh,
..
} = *self;
let [vp_l, vp_t, vp_r, vp_b] = viewport;
let lt = [vp_l as Scalar, vp_t as Scalar];
let rb = [vp_r as Scalar, vp_b as Scalar];
let viewport = Rect::from_corners(lt, rb);
let fill = mesh.fill(viewport, dpi_factor, image_map, primitives)?;
let glyph_cache_cmd = match fill.glyph_cache_requires_upload {
false => None,
true => Some(GlyphCacheCommand {
glyph_cache_pixel_buffer: mesh.glyph_cache_pixel_buffer(),
glyph_cpu_buffer_pool: glyph_uploads.clone(),
glyph_cache_texture: glyph_cache_tex.clone(),
}),
};
Ok(glyph_cache_cmd)
}
pub fn draw(
&mut self,
queue: Arc<Queue>,
image_map: &image::Map<Image>,
viewport: [f32; 4],
) -> Result<Vec<DrawCommand>, DrawError> {
let current_viewport = Viewport {
origin: [viewport[0], viewport[1]],
dimensions: [viewport[2] - viewport[0], viewport[3] - viewport[1]],
depth_range: 0.0..1.0,
};
let mut current_scizzor = Scissor {
origin: [viewport[0] as u32, viewport[1] as u32],
dimensions: [
(viewport[2] - viewport[0]) as u32,
(viewport[3] - viewport[1]) as u32,
],
};
let conv_scizzor = |s: mesh::Scizzor| Scissor {
origin: [s.top_left[0] as u32, s.top_left[1] as u32],
dimensions: s.dimensions,
};
let mut desc_set_builder = self.tex_descs.next();
desc_set_builder.add_sampled_image(
ImageView::new(self.glyph_cache_tex.clone()).unwrap(),
self.sampler.clone(),
)?;
let desc_cache = Arc::new(desc_set_builder.build()?);
let commands = self.mesh.commands();
let mut draw_commands = vec![];
for command in commands {
match command {
mesh::Command::Scizzor(scizzor) => current_scizzor = conv_scizzor(scizzor),
mesh::Command::Draw(draw) => match draw {
mesh::Draw::Plain(vert_range) => {
if !vert_range.is_empty() {
let verts = &self.mesh.vertices()[vert_range];
let verts = conv_vertex_buffer(verts);
let (vbuf, vbuf_fut) = ImmutableBuffer::<[Vertex]>::from_iter(
verts.iter().cloned(),
BufferUsage::vertex_buffer(),
queue.clone(),
)?;
vbuf_fut
.then_signal_fence_and_flush()
.expect("failed to flush future")
.wait(None)
.unwrap();
draw_commands.push(DrawCommand {
graphics_pipeline: self.pipeline.clone(),
scissor: current_scizzor,
viewport: current_viewport.clone(),
vertex_buffer: vbuf,
descriptor_set: desc_cache.clone(),
});
}
}
mesh::Draw::Image(image_id, vert_range) => {
if vert_range.is_empty() {
continue;
}
if let Some(image) = image_map.get(&image_id) {
let mut desc_set_builder = self.tex_descs.next();
desc_set_builder.add_sampled_image(
ImageView::new(image.image_access.clone()).unwrap(),
self.sampler.clone(),
)?;
let desc_image = Arc::new(desc_set_builder.build()?);
let verts = &self.mesh.vertices()[vert_range];
let verts = conv_vertex_buffer(verts);
let (vbuf, vbuf_fut) = ImmutableBuffer::from_iter(
verts.iter().cloned(),
BufferUsage::vertex_buffer(),
queue.clone(),
)?;
vbuf_fut
.then_signal_fence_and_flush()
.unwrap()
.wait(None)
.unwrap();
draw_commands.push(DrawCommand {
graphics_pipeline: self.pipeline.clone(),
scissor: current_scizzor,
viewport: current_viewport.clone(),
vertex_buffer: vbuf,
descriptor_set: desc_image,
});
}
}
},
}
}
Ok(draw_commands)
}
}
fn conv_vertex_buffer(buffer: &[mesh::Vertex]) -> &[Vertex] {
unsafe { &*(buffer as *const [conrod_core::mesh::Vertex] as *const [Vertex]) }
}
impl From<vulkano::OomError> for RendererCreationError {
fn from(err: OomError) -> Self {
RendererCreationError::ShaderLoad(err)
}
}
impl From<SamplerCreationError> for RendererCreationError {
fn from(err: SamplerCreationError) -> Self {
RendererCreationError::SamplerCreation(err)
}
}
impl From<GraphicsPipelineCreationError> for RendererCreationError {
fn from(err: GraphicsPipelineCreationError) -> Self {
RendererCreationError::GraphicsPipelineCreation(err)
}
}
impl From<ImageCreationError> for RendererCreationError {
fn from(err: ImageCreationError) -> Self {
RendererCreationError::ImageCreation(err)
}
}
impl From<DescriptorSetError> for DrawError {
fn from(err: DescriptorSetError) -> Self {
DrawError::DescriptorSet(err)
}
}
impl From<DeviceMemoryAllocError> for DrawError {
fn from(err: DeviceMemoryAllocError) -> Self {
DrawError::VertexBufferAlloc(err)
}
}
impl StdError for RendererCreationError {
fn cause(&self) -> Option<&dyn StdError> {
match *self {
RendererCreationError::SamplerCreation(ref err) => Some(err),
RendererCreationError::ShaderLoad(ref err) => Some(err),
RendererCreationError::GraphicsPipelineCreation(ref err) => Some(err),
RendererCreationError::ImageCreation(ref err) => Some(err),
}
}
}
impl StdError for DrawError {
fn cause(&self) -> Option<&dyn StdError> {
match *self {
DrawError::DescriptorSet(ref err) => Some(err),
DrawError::VertexBufferAlloc(ref err) => Some(err),
}
}
}
impl fmt::Display for RendererCreationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
RendererCreationError::SamplerCreation(ref err) => err.fmt(f),
RendererCreationError::ShaderLoad(ref err) => err.fmt(f),
RendererCreationError::GraphicsPipelineCreation(ref err) => err.fmt(f),
RendererCreationError::ImageCreation(ref err) => err.fmt(f),
}
}
}
impl fmt::Display for DrawError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
DrawError::DescriptorSet(ref err) => err.fmt(f),
DrawError::VertexBufferAlloc(ref err) => err.fmt(f),
}
}
}