pub use self::conrod_core::event::Input;
pub use self::conrod_core::{
color, cursor, event, graph, image, input, position, scroll, text, theme, utils, widget,
};
pub use self::conrod_core::{
Borderable, Bordering, Color, Colorable, Dimensions, FontSize, Labelable, Point, Positionable,
Range, Rect, Scalar, Sizeable, Theme, UiCell, Widget,
};
pub use crate::conrod_core;
pub use crate::conrod_vulkano;
pub use crate::conrod_winit;
pub mod prelude {
pub use super::{Borderable, Colorable, Labelable, Positionable, Sizeable, Widget};
pub use super::{
Bordering, Dimensions, FontSize, Input, Point, Range, Rect, Scalar, Theme, Ui, UiCell,
};
pub use super::{color, image, position, text, widget};
}
use self::conrod_core::text::rt::gpu_cache::CacheWriteErr;
use self::conrod_vulkano::RendererCreationError;
use crate::frame::{Frame, ViewFbo};
use crate::vk;
use crate::window::{self, Window};
use crate::App;
use std::cell::RefCell;
use std::collections::HashMap;
use std::error::Error as StdError;
use std::fmt;
use std::ops::Deref;
use std::path::PathBuf;
use std::sync::{mpsc, Arc, Mutex};
use winit;
pub(crate) struct Arrangement {
pub(super) windows: RefCell<HashMap<window::Id, Vec<Handle>>>,
}
pub(crate) struct Handle {
pub(crate) input_tx: Option<mpsc::SyncSender<Input>>,
}
pub struct Ui {
window_id: window::Id,
ui: conrod_core::Ui,
input_rx: Option<mpsc::Receiver<Input>>,
pub image_map: ImageMap,
renderer: Mutex<conrod_vulkano::Renderer>,
render_mode: Mutex<RenderMode>,
}
enum RenderMode {
Subpass,
OwnedRenderTarget(RenderTarget),
}
struct RenderTarget {
render_pass: Arc<vk::RenderPassAbstract + Send + Sync>,
images: RenderPassImages,
view_fbo: ViewFbo,
}
struct RenderPassImages {
depth: Arc<vk::AttachmentImage<DepthFormat>>,
}
pub struct Builder<'a> {
app: &'a App,
window_id: Option<window::Id>,
dimensions: Option<[Scalar; 2]>,
theme: Option<Theme>,
automatically_handle_input: bool,
pending_input_limit: usize,
default_font_path: Option<PathBuf>,
glyph_cache_dimensions: Option<(u32, u32)>,
render_pass_subpass: Option<Subpass>,
}
#[derive(Debug)]
pub enum BuildError {
InvalidWindow,
RendererCreation(RendererCreationError),
RenderTargetCreation(RenderTargetCreationError),
}
#[derive(Debug)]
pub enum RenderTargetCreationError {
RenderPassCreation(vk::RenderPassCreationError),
ImageCreation(vk::ImageCreationError),
}
#[derive(Debug)]
pub enum DrawToFrameError {
InvalidWindow,
RendererPoisoned,
RenderModePoisoned,
InvalidRenderMode,
ImageCreation(vk::ImageCreationError),
FramebufferCreation(vk::FramebufferCreationError),
RendererFill(CacheWriteErr),
GlyphPixelDataUpload(vk::memory::DeviceMemoryAllocError),
CopyBufferImageCommand(vk::command_buffer::CopyBufferImageError),
RendererDraw(conrod_vulkano::DrawError),
DrawCommand(vk::command_buffer::DrawError),
}
pub type Subpass = vk::framebuffer::Subpass<Arc<vk::RenderPassAbstract + Send + Sync>>;
pub type ImageMap = conrod_core::image::Map<conrod_vulkano::Image>;
pub type DepthFormat = vk::format::D16Unorm;
pub const DEPTH_FORMAT_TY: DepthFormat = vk::format::D16Unorm;
pub const DEPTH_FORMAT: vk::Format = vk::Format::D16Unorm;
impl conrod_winit::WinitWindow for Window {
fn get_inner_size(&self) -> Option<(u32, u32)> {
let (w, h) = self.inner_size_points();
Some((w as _, h as _))
}
fn hidpi_factor(&self) -> f32 {
self.hidpi_factor()
}
}
impl Arrangement {
pub(crate) fn new() -> Self {
let windows = RefCell::new(HashMap::new());
Arrangement { windows }
}
}
impl<'a> Builder<'a> {
pub(super) fn new(app: &'a App) -> Self {
Builder {
app,
window_id: None,
dimensions: None,
theme: None,
automatically_handle_input: true,
pending_input_limit: Ui::DEFAULT_PENDING_INPUT_LIMIT,
default_font_path: None,
glyph_cache_dimensions: None,
render_pass_subpass: None,
}
}
pub fn window(mut self, window_id: window::Id) -> Self {
self.window_id = Some(window_id);
self
}
pub fn with_dimensions(mut self, dimensions: [Scalar; 2]) -> Self {
self.dimensions = Some(dimensions);
self
}
pub fn with_theme(mut self, theme: Theme) -> Self {
self.theme = Some(theme);
self
}
pub fn automatically_handle_input(mut self, b: bool) -> Self {
self.automatically_handle_input = b;
self
}
pub fn pending_input_limit(mut self, limit: usize) -> Self {
self.pending_input_limit = limit;
self
}
pub fn default_font_path(mut self, path: PathBuf) -> Self {
self.default_font_path = Some(path);
self
}
pub fn with_glyph_cache_dimensions(mut self, width: u32, height: u32) -> Self {
self.glyph_cache_dimensions = Some((width, height));
self
}
pub fn subpass(mut self, subpass: Subpass) -> Self {
self.render_pass_subpass = Some(subpass);
self
}
pub fn build(self) -> Result<Ui, BuildError> {
let Builder {
app,
window_id,
dimensions,
theme,
pending_input_limit,
automatically_handle_input,
default_font_path,
glyph_cache_dimensions,
render_pass_subpass,
} = self;
let window_id = window_id.unwrap_or(app.window_id());
let window = app.window(window_id).ok_or(BuildError::InvalidWindow)?;
let dimensions = dimensions.unwrap_or_else(|| {
let (win_w, win_h) = window.inner_size_points();
[win_w as Scalar, win_h as Scalar]
});
let theme = theme.unwrap_or_else(Theme::default);
let ui = conrod_core::UiBuilder::new(dimensions).theme(theme).build();
let (input_rx, handle) = if automatically_handle_input {
let (input_tx, input_rx) = mpsc::sync_channel(pending_input_limit);
let input_tx = Some(input_tx);
let input_rx = Some(input_rx);
let handle = Handle { input_tx };
(input_rx, handle)
} else {
let input_tx = None;
let input_rx = None;
let handle = Handle { input_tx };
(input_rx, handle)
};
app.ui
.windows
.borrow_mut()
.entry(window_id)
.or_insert(Vec::new())
.push(handle);
let (subpass, render_mode) = match render_pass_subpass {
Some(subpass) => (subpass, Mutex::new(RenderMode::Subpass)),
None => {
let render_target = RenderTarget::new(&window)?;
let subpass = Subpass::from(render_target.render_pass.clone(), 0)
.expect("unable to retrieve subpass for index `0`");
let render_mode = Mutex::new(RenderMode::OwnedRenderTarget(render_target));
(subpass, render_mode)
}
};
let device = window.swapchain_device().clone();
let queue = window.swapchain_queue().clone();
let renderer = match glyph_cache_dimensions {
Some((w, h)) => conrod_vulkano::Renderer::with_glyph_cache_dimensions(
device,
subpass,
queue.family(),
[w as _, h as _],
)?,
None => conrod_vulkano::Renderer::new(
device,
subpass,
queue.family(),
[dimensions[0] as _, dimensions[1] as _],
window.hidpi_factor() as _,
)?,
};
let renderer = Mutex::new(renderer);
let image_map = image::Map::new();
let mut ui = Ui {
window_id,
ui,
input_rx,
image_map,
renderer,
render_mode,
};
let default_font_path = default_font_path.or_else(|| {
app.assets_path()
.ok()
.map(|p| p.join(Ui::DEFAULT_FONT_PATH))
});
if let Some(font_path) = default_font_path {
ui.fonts_mut().insert_from_file(font_path).ok();
}
Ok(ui)
}
}
pub fn create_render_pass(
device: Arc<vk::device::Device>,
color_format: vk::Format,
depth_format: vk::Format,
msaa_samples: u32,
) -> Result<Arc<vk::RenderPassAbstract + Send + Sync>, vk::RenderPassCreationError> {
let render_pass = vk::single_pass_renderpass!(
device,
attachments: {
color: {
load: Load,
store: Store,
format: color_format,
samples: msaa_samples,
},
depth: {
load: Load,
store: Store,
format: depth_format,
samples: msaa_samples,
}
},
pass: {
color: [color],
depth_stencil: {depth}
}
)?;
Ok(Arc::new(render_pass))
}
impl RenderPassImages {
fn new(window: &Window) -> Result<Self, vk::ImageCreationError> {
let device = window.swapchain_device().clone();
let image_dims = window.swapchain_images()[0].dimensions();
let msaa_samples = window.msaa_samples();
let depth = vk::AttachmentImage::transient_multisampled(
device.clone(),
image_dims,
msaa_samples,
DEPTH_FORMAT_TY,
)?;
Ok(RenderPassImages { depth })
}
}
impl RenderTarget {
fn new(window: &Window) -> Result<Self, RenderTargetCreationError> {
let device = window.swapchain_device().clone();
let color_format = window.swapchain().format();
let msaa_samples = window.msaa_samples();
let render_pass = create_render_pass(device, color_format, DEPTH_FORMAT, msaa_samples)?;
let images = RenderPassImages::new(window)?;
let view_fbo = ViewFbo::default();
Ok(RenderTarget {
render_pass,
images,
view_fbo,
})
}
}
impl Deref for Ui {
type Target = conrod_core::Ui;
fn deref(&self) -> &Self::Target {
&self.ui
}
}
impl Ui {
pub const DEFAULT_PENDING_INPUT_LIMIT: usize = 1024;
pub const DEFAULT_FONT_PATH: &'static str = "fonts/NotoSans/NotoSans-Regular.ttf";
pub fn generate_widget_id(&mut self) -> widget::Id {
self.widget_id_generator().next()
}
pub fn widget_id_generator(&mut self) -> widget::id::Generator {
self.ui.widget_id_generator()
}
pub fn handle_input(&mut self, input: Input) {
self.ui.handle_event(input)
}
pub fn handle_pending_input(&mut self) {
let Ui {
ref mut ui,
ref input_rx,
..
} = *self;
if let Some(ref rx) = *input_rx {
for input in rx.try_iter() {
ui.handle_event(input);
}
}
}
pub fn set_widgets(&mut self) -> UiCell {
self.handle_pending_input();
self.ui.set_widgets()
}
pub fn fonts_mut(&mut self) -> &mut text::font::Map {
&mut self.ui.fonts
}
pub fn theme_mut(&mut self) -> &mut Theme {
&mut self.ui.theme
}
pub fn clear_with(&mut self, color: Color) {
self.ui.clear_with(color)
}
pub fn draw_to_frame(&self, app: &App, frame: &Frame) -> Result<(), DrawToFrameError> {
let primitives = self.ui.draw();
draw_primitives(self, app, frame, primitives)
}
pub fn draw_to_frame_if_changed(
&self,
app: &App,
frame: &Frame,
) -> Result<bool, DrawToFrameError> {
match self.ui.draw_if_changed() {
None => Ok(false),
Some(primitives) => draw_primitives(self, app, frame, primitives).map(|()| true),
}
}
}
pub fn draw_primitives(
ui: &Ui,
app: &App,
frame: &Frame,
primitives: conrod_core::render::Primitives,
) -> Result<(), DrawToFrameError> {
let Ui {
ref renderer,
ref render_mode,
ref image_map,
window_id,
..
} = *ui;
let window = match app.window(window_id) {
Some(window) => window,
None => return Err(DrawToFrameError::InvalidWindow),
};
let mut renderer = renderer
.lock()
.map_err(|_| DrawToFrameError::RendererPoisoned)?;
let mut render_mode = render_mode
.lock()
.map_err(|_| DrawToFrameError::RenderModePoisoned)?;
let render_target = match *render_mode {
RenderMode::Subpass => return Err(DrawToFrameError::InvalidRenderMode),
RenderMode::OwnedRenderTarget(ref mut render_target) => render_target,
};
let RenderTarget {
ref render_pass,
ref mut images,
ref mut view_fbo,
} = *render_target;
let image_dims = frame.swapchain_image().dimensions();
if image_dims != images.depth.dimensions() {
*images = RenderPassImages::new(&window)?;
}
view_fbo.update(&frame, render_pass.clone(), |builder, image| {
builder.add(image.clone())?.add(images.depth.clone())
})?;
let (win_w, win_h) = window.inner_size_pixels();
let dpi_factor = window.hidpi_factor() as f64;
let viewport = [0.0, 0.0, win_w as f32, win_h as f32];
if let Some(cmd) = renderer.fill(image_map, viewport, dpi_factor, primitives)? {
let buffer = cmd
.glyph_cpu_buffer_pool
.chunk(cmd.glyph_cache_pixel_buffer.iter().cloned())?;
frame
.add_commands()
.copy_buffer_to_image(buffer, cmd.glyph_cache_texture)?;
}
let queue = window.swapchain_queue().clone();
let cmds = renderer.draw(queue, image_map, viewport)?;
if !cmds.is_empty() {
let color = vk::ClearValue::None;
let depth = vk::ClearValue::None;
let clear_values = vec![color, depth];
frame
.add_commands()
.begin_render_pass(view_fbo.expect_inner(), false, clear_values.clone())
.unwrap();
for cmd in cmds {
let conrod_vulkano::DrawCommand {
graphics_pipeline,
dynamic_state,
vertex_buffer,
descriptor_set,
} = cmd;
frame.add_commands().draw(
graphics_pipeline,
&dynamic_state,
vec![vertex_buffer],
descriptor_set,
(),
)?;
}
frame.add_commands().end_render_pass().unwrap();
}
Ok(())
}
mod conrod_winit_conv {
use crate::conrod_winit::*;
conrod_winit::conversion_fns!();
}
pub fn winit_window_event_to_input(event: winit::WindowEvent, window: &Window) -> Option<Input> {
conrod_winit_conv::convert_window_event(event, window)
}
impl From<RenderTargetCreationError> for BuildError {
fn from(err: RenderTargetCreationError) -> Self {
BuildError::RenderTargetCreation(err)
}
}
impl From<RendererCreationError> for BuildError {
fn from(err: RendererCreationError) -> Self {
BuildError::RendererCreation(err)
}
}
impl From<vk::ImageCreationError> for RenderTargetCreationError {
fn from(err: vk::ImageCreationError) -> Self {
RenderTargetCreationError::ImageCreation(err)
}
}
impl From<vk::RenderPassCreationError> for RenderTargetCreationError {
fn from(err: vk::RenderPassCreationError) -> Self {
RenderTargetCreationError::RenderPassCreation(err)
}
}
impl From<vk::ImageCreationError> for DrawToFrameError {
fn from(err: vk::ImageCreationError) -> Self {
DrawToFrameError::ImageCreation(err)
}
}
impl From<vk::FramebufferCreationError> for DrawToFrameError {
fn from(err: vk::FramebufferCreationError) -> Self {
DrawToFrameError::FramebufferCreation(err)
}
}
impl From<CacheWriteErr> for DrawToFrameError {
fn from(err: CacheWriteErr) -> Self {
DrawToFrameError::RendererFill(err)
}
}
impl From<vk::memory::DeviceMemoryAllocError> for DrawToFrameError {
fn from(err: vk::memory::DeviceMemoryAllocError) -> Self {
DrawToFrameError::GlyphPixelDataUpload(err)
}
}
impl From<vk::command_buffer::CopyBufferImageError> for DrawToFrameError {
fn from(err: vk::command_buffer::CopyBufferImageError) -> Self {
DrawToFrameError::CopyBufferImageCommand(err)
}
}
impl From<conrod_vulkano::DrawError> for DrawToFrameError {
fn from(err: conrod_vulkano::DrawError) -> Self {
DrawToFrameError::RendererDraw(err)
}
}
impl From<vk::command_buffer::DrawError> for DrawToFrameError {
fn from(err: vk::command_buffer::DrawError) -> Self {
DrawToFrameError::DrawCommand(err)
}
}
impl StdError for BuildError {
fn description(&self) -> &str {
match *self {
BuildError::InvalidWindow => "no open window associated with the given `window_id`",
BuildError::RendererCreation(ref err) => err.description(),
BuildError::RenderTargetCreation(ref err) => err.description(),
}
}
fn cause(&self) -> Option<&StdError> {
match *self {
BuildError::InvalidWindow => None,
BuildError::RendererCreation(ref err) => Some(err),
BuildError::RenderTargetCreation(ref err) => Some(err),
}
}
}
impl StdError for RenderTargetCreationError {
fn description(&self) -> &str {
match *self {
RenderTargetCreationError::RenderPassCreation(ref err) => err.description(),
RenderTargetCreationError::ImageCreation(ref err) => err.description(),
}
}
fn cause(&self) -> Option<&StdError> {
match *self {
RenderTargetCreationError::RenderPassCreation(ref err) => Some(err),
RenderTargetCreationError::ImageCreation(ref err) => Some(err),
}
}
}
impl StdError for DrawToFrameError {
fn description(&self) -> &str {
match *self {
DrawToFrameError::InvalidWindow => {
"no open window associated with the given `window_id`"
}
DrawToFrameError::RendererPoisoned => "`Mutex` containing `Renderer` was poisoned",
DrawToFrameError::RenderModePoisoned => "`Mutex` containing `RenderMode` was poisoned",
DrawToFrameError::InvalidRenderMode => {
"`draw_to_frame` was called while `Ui` was in `Subpass` render mode"
}
DrawToFrameError::ImageCreation(ref err) => err.description(),
DrawToFrameError::FramebufferCreation(ref err) => err.description(),
DrawToFrameError::RendererFill(ref err) => err.description(),
DrawToFrameError::GlyphPixelDataUpload(ref err) => err.description(),
DrawToFrameError::CopyBufferImageCommand(ref err) => err.description(),
DrawToFrameError::RendererDraw(ref err) => err.description(),
DrawToFrameError::DrawCommand(ref err) => err.description(),
}
}
fn cause(&self) -> Option<&StdError> {
match *self {
DrawToFrameError::InvalidWindow => None,
DrawToFrameError::RendererPoisoned => None,
DrawToFrameError::RenderModePoisoned => None,
DrawToFrameError::InvalidRenderMode => None,
DrawToFrameError::ImageCreation(ref err) => Some(err),
DrawToFrameError::FramebufferCreation(ref err) => Some(err),
DrawToFrameError::RendererFill(ref err) => Some(err),
DrawToFrameError::GlyphPixelDataUpload(ref err) => Some(err),
DrawToFrameError::CopyBufferImageCommand(ref err) => Some(err),
DrawToFrameError::RendererDraw(ref err) => Some(err),
DrawToFrameError::DrawCommand(ref err) => Some(err),
}
}
}
impl fmt::Display for BuildError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.description())
}
}
impl fmt::Display for RenderTargetCreationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.description())
}
}
impl fmt::Display for DrawToFrameError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.description())
}
}