conrod_vulkano/
lib.rs

1extern crate conrod_core;
2#[macro_use]
3extern crate vulkano;
4
5use std::error::Error as StdError;
6use std::fmt;
7use std::sync::Arc;
8
9use conrod_core::mesh::{self, Mesh};
10use conrod_core::text::rt;
11use conrod_core::{image, render, Rect, Scalar};
12use std::ffi::CString;
13use vulkano::buffer::{BufferUsage, CpuBufferPool, ImmutableBuffer};
14use vulkano::descriptor_set::layout::{
15    DescriptorDesc, DescriptorDescImage, DescriptorDescTy, DescriptorSetDesc, DescriptorSetLayout,
16};
17use vulkano::descriptor_set::{DescriptorSet, DescriptorSetError, SingleLayoutDescSetPool};
18use vulkano::device::physical::QueueFamily;
19use vulkano::device::{Device, Queue};
20use vulkano::format::Format;
21use vulkano::image::view::{ImageView, ImageViewType};
22use vulkano::image::{
23    ImageCreateFlags, ImageCreationError, ImageDimensions, ImageUsage, ImmutableImage, StorageImage,
24};
25use vulkano::memory::DeviceMemoryAllocError;
26use vulkano::pipeline::layout::PipelineLayout;
27use vulkano::pipeline::shader::{
28    GraphicsShaderType, ShaderInterface, ShaderInterfaceEntry, ShaderModule, ShaderStages,
29    SpecializationConstants,
30};
31use vulkano::pipeline::viewport::{Scissor, Viewport};
32use vulkano::pipeline::{GraphicsPipeline, GraphicsPipelineCreationError};
33use vulkano::render_pass::Subpass;
34use vulkano::sampler::{Filter, MipmapMode, Sampler, SamplerAddressMode, SamplerCreationError};
35use vulkano::sync::GpuFuture;
36use vulkano::OomError;
37
38/// A loaded vulkan texture and it's width/height
39pub struct Image {
40    /// The immutable image type, represents the data loaded onto the GPU.
41    ///
42    /// Uses a dynamic format for flexibility on the kinds of images that might be loaded.
43    pub image_access: Arc<ImmutableImage>,
44    /// The width of the image.
45    pub width: u32,
46    /// The height of the image.
47    pub height: u32,
48}
49
50/// The data associated with a single vertex.
51#[repr(C)]
52#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)]
53pub struct Vertex {
54    /// The normalised position of the vertex within vector space.
55    ///
56    /// [-1.0, 1.0] is the leftmost, bottom position of the display.
57    /// [1.0, -1.0] is the rightmost, top position of the display.
58    pub position: [f32; 2],
59    /// The coordinates of the texture used by this `Vertex`.
60    ///
61    /// [0.0, 0.0] is the leftmost, top position of the texture.
62    /// [1.0, 1.0] is the rightmost, bottom position of the texture.
63    pub tex_coords: [f32; 2],
64    /// Linear sRGB with an alpha channel.
65    pub rgba: [f32; 4],
66    /// The mode with which the `Vertex` will be drawn within the fragment shader.
67    ///
68    /// `0` for rendering text.
69    /// `1` for rendering an image.
70    /// `2` for rendering non-textured 2D geometry.
71    ///
72    /// If any other value is given, the fragment shader will not output any color.
73    pub mode: u32,
74}
75
76impl_vertex!(Vertex, position, tex_coords, rgba, mode);
77//shader interface def entries
78fn create_shader_interface_vs_in() -> ShaderInterface {
79    unsafe {
80        ShaderInterface::new_unchecked(vec![
81            ShaderInterfaceEntry {
82                location: 0..1,
83                format: Format::R32G32_SFLOAT,
84                name: Some(std::borrow::Cow::Borrowed("position")),
85            },
86            ShaderInterfaceEntry {
87                location: 1..2,
88                format: Format::R32G32_SFLOAT,
89                name: Some(std::borrow::Cow::Borrowed("tex_coords")),
90            },
91            ShaderInterfaceEntry {
92                location: 2..3,
93                format: Format::R32G32B32A32_SFLOAT,
94                name: Some(std::borrow::Cow::Borrowed("rgba")),
95            },
96            ShaderInterfaceEntry {
97                location: 3..4,
98                format: Format::R32_UINT,
99                name: Some(std::borrow::Cow::Borrowed("mode")),
100            },
101        ])
102    }
103}
104fn create_shader_interface_vs_out() -> ShaderInterface {
105    unsafe {
106        ShaderInterface::new_unchecked(vec![
107            ShaderInterfaceEntry {
108                location: 0..1,
109                format: Format::R32G32_SFLOAT,
110                name: Some(std::borrow::Cow::Borrowed("v_Uv")),
111            },
112            ShaderInterfaceEntry {
113                location: 1..2,
114                format: Format::R32G32B32A32_SFLOAT,
115                name: Some(std::borrow::Cow::Borrowed("v_Color")),
116            },
117            ShaderInterfaceEntry {
118                location: 2..3,
119                format: Format::R32_UINT,
120                name: Some(std::borrow::Cow::Borrowed("v_Mode")),
121            },
122        ])
123    }
124}
125fn create_shader_interface_fs_out() -> ShaderInterface {
126    unsafe {
127        ShaderInterface::new_unchecked(vec![ShaderInterfaceEntry {
128            location: 0..1,
129            format: Format::R32G32B32A32_SFLOAT,
130            name: Some(std::borrow::Cow::Borrowed("Target0")),
131        }])
132    }
133}
134
135/// A type used for translating `render::Primitives` into `Command`s that indicate how to draw the
136/// conrod GUI using `vulkano`.
137pub struct Renderer {
138    pipeline: Arc<GraphicsPipeline>,
139    glyph_uploads: Arc<CpuBufferPool<u8>>,
140    glyph_cache_tex: Arc<StorageImage>,
141    sampler: Arc<Sampler>,
142    tex_descs: SingleLayoutDescSetPool,
143    mesh: Mesh,
144}
145
146/// An command for uploading an individual glyph.
147pub struct GlyphCacheCommand<'a> {
148    /// The CPU buffer containing the pixel data.
149    pub glyph_cache_pixel_buffer: &'a [u8],
150    /// The cpu buffer pool used to upload glyph pixels.
151    pub glyph_cpu_buffer_pool: Arc<CpuBufferPool<u8>>,
152    /// The GPU image to which the glyphs are cached.
153    pub glyph_cache_texture: Arc<StorageImage>,
154}
155
156/// A draw command that maps directly to the `AutoCommandBufferBuilder::draw` method. By returning
157/// `DrawCommand`s, we can avoid consuming the entire `AutoCommandBufferBuilder` itself which might
158/// not always be available from APIs that wrap Vulkan.
159pub struct DrawCommand {
160    pub graphics_pipeline: Arc<GraphicsPipeline>,
161    pub scissor: Scissor,
162    pub viewport: Viewport,
163    pub descriptor_set: Arc<dyn DescriptorSet + Send + Sync>,
164    pub vertex_buffer: Arc<ImmutableBuffer<[Vertex]>>,
165}
166
167/// Errors that might occur during creation of the renderer.
168#[derive(Debug)]
169pub enum RendererCreationError {
170    SamplerCreation(SamplerCreationError),
171    ShaderLoad(vulkano::OomError),
172    GraphicsPipelineCreation(GraphicsPipelineCreationError),
173    ImageCreation(ImageCreationError),
174}
175
176/// Errors that might occur during draw calls.
177#[derive(Debug)]
178pub enum DrawError {
179    DescriptorSet(DescriptorSetError),
180
181    VertexBufferAlloc(DeviceMemoryAllocError),
182}
183
184impl mesh::ImageDimensions for Image {
185    fn dimensions(&self) -> [u32; 2] {
186        [self.width, self.height]
187    }
188}
189
190impl Renderer {
191    /// Construct a new empty `Renderer`.
192    ///
193    /// The dimensions of the glyph cache will be the dimensions of the window multiplied by the
194    /// DPI factor.
195    pub fn new(
196        device: Arc<Device>,
197        subpass: Subpass,
198        graphics_queue_family: QueueFamily,
199        window_dims: [u32; 2],
200        dpi_factor: f64,
201    ) -> Result<Self, RendererCreationError> {
202        // TODO: Check that necessary subpass properties exist?
203        let [w, h] = window_dims;
204        let glyph_cache_dims = [
205            (w as f64 * dpi_factor) as u32,
206            (h as f64 * dpi_factor) as u32,
207        ];
208        Self::with_glyph_cache_dimensions(device, subpass, graphics_queue_family, glyph_cache_dims)
209    }
210
211    /// Construct a new empty `Renderer`.
212    pub fn with_glyph_cache_dimensions(
213        device: Arc<Device>,
214        subpass: Subpass,
215        graphics_queue_family: QueueFamily,
216        glyph_cache_dims: [u32; 2],
217    ) -> Result<Self, RendererCreationError> {
218        let sampler = Sampler::new(
219            device.clone(),
220            Filter::Linear,
221            Filter::Linear,
222            MipmapMode::Nearest,
223            SamplerAddressMode::ClampToEdge,
224            SamplerAddressMode::ClampToEdge,
225            SamplerAddressMode::ClampToEdge,
226            0.0,
227            1.0,
228            0.0,
229            0.0,
230        )?;
231        let descriptor_set_desc = DescriptorSetDesc::new(vec![Some(DescriptorDesc {
232            ty: DescriptorDescTy::CombinedImageSampler {
233                image_desc: DescriptorDescImage {
234                    format: None,
235                    multisampled: false,
236                    view_type: ImageViewType::Dim2d,
237                },
238                immutable_samplers: vec![],
239            },
240            stages: ShaderStages {
241                vertex: false,
242                tessellation_control: false,
243                tessellation_evaluation: false,
244                geometry: false,
245                fragment: true,
246                compute: false,
247            },
248            variable_count: false,
249            descriptor_count: 1,
250            mutable: false,
251        })]);
252        let descriptor_set_layout = Arc::new(
253            DescriptorSetLayout::new(device.clone(), descriptor_set_desc.clone()).unwrap(),
254        );
255        let layout = Arc::new(
256            PipelineLayout::new(device.clone(), vec![descriptor_set_layout], vec![]).unwrap(),
257        );
258        let vs_module =
259            unsafe { ShaderModule::new(device.clone(), include_bytes!("shaders/vert.spv")) }?;
260
261        let fs_module =
262            unsafe { ShaderModule::new(device.clone(), include_bytes!("shaders/frag.spv")) }?;
263        let main = CString::new("main").unwrap();
264        let vs_in = create_shader_interface_vs_in();
265        let vs_out = create_shader_interface_vs_out();
266        let fs_out = create_shader_interface_fs_out();
267        let vs = unsafe {
268            vs_module.graphics_entry_point(
269                &main,
270                vec![descriptor_set_desc.clone()],
271                None,
272                <()>::descriptors(),
273                vs_in,
274                vs_out.clone(),
275                GraphicsShaderType::Vertex,
276            )
277        };
278        let fs = unsafe {
279            fs_module.graphics_entry_point(
280                &main,
281                vec![descriptor_set_desc.clone()],
282                None,
283                <()>::descriptors(),
284                vs_out,
285                fs_out,
286                GraphicsShaderType::Fragment,
287            )
288        };
289
290        let pipeline = Arc::new(
291            GraphicsPipeline::start()
292                .vertex_input_single_buffer::<Vertex>()
293                .vertex_shader(vs, ())
294                .depth_stencil_disabled()
295                .triangle_list()
296                .front_face_clockwise()
297                .viewports_scissors_dynamic(1)
298                .fragment_shader(fs, ())
299                .blend_alpha_blending()
300                .render_pass(subpass)
301                .with_pipeline_layout(device.clone(), layout)?,
302        );
303        let mesh = Mesh::with_glyph_cache_dimensions(glyph_cache_dims);
304
305        let glyph_cache_tex = {
306            let [width, height] = glyph_cache_dims;
307            StorageImage::with_usage(
308                device.clone(),
309                ImageDimensions::Dim2d {
310                    width,
311                    height,
312                    array_layers: 1,
313                },
314                Format::R8_UNORM,
315                ImageUsage {
316                    transfer_destination: true,
317                    sampled: true,
318                    ..ImageUsage::none()
319                },
320                ImageCreateFlags {
321                    sparse_binding: false,
322                    sparse_residency: false,
323                    sparse_aliased: false,
324                    mutable_format: false,
325                    cube_compatible: false,
326                    array_2d_compatible: false,
327                    block_texel_view_compatible: false,
328                },
329                vec![graphics_queue_family],
330            )?
331        };
332
333        let tex_descs =
334            SingleLayoutDescSetPool::new(pipeline.layout().descriptor_set_layouts()[0].clone());
335        let glyph_uploads = Arc::new(CpuBufferPool::upload(device));
336
337        Ok(Renderer {
338            pipeline,
339            glyph_uploads,
340            glyph_cache_tex,
341            sampler,
342            tex_descs,
343            mesh,
344        })
345    }
346
347    /// Produce an `Iterator` yielding `Command`s.
348    pub fn commands(&self) -> mesh::Commands {
349        self.mesh.commands()
350    }
351
352    /// Fill the inner vertex and command buffers by translating the given `primitives`.
353    ///
354    /// This method may return an `Option<GlyphCacheCommand>`, in which case the user should use
355    /// the contained `glyph_cpu_buffer_pool` to write the pixel data to the GPU, and then use a
356    /// `copy_buffer_to_image` command to write the data to the given `glyph_cache_texture` image.
357    pub fn fill<P: render::PrimitiveWalker>(
358        &mut self,
359        image_map: &image::Map<Image>,
360        viewport: [f32; 4],
361        dpi_factor: f64,
362        primitives: P,
363    ) -> Result<Option<GlyphCacheCommand>, rt::gpu_cache::CacheWriteErr> {
364        let Renderer {
365            ref glyph_uploads,
366            ref glyph_cache_tex,
367            ref mut mesh,
368            ..
369        } = *self;
370        let [vp_l, vp_t, vp_r, vp_b] = viewport;
371        let lt = [vp_l as Scalar, vp_t as Scalar];
372        let rb = [vp_r as Scalar, vp_b as Scalar];
373        let viewport = Rect::from_corners(lt, rb);
374        let fill = mesh.fill(viewport, dpi_factor, image_map, primitives)?;
375        let glyph_cache_cmd = match fill.glyph_cache_requires_upload {
376            false => None,
377            true => Some(GlyphCacheCommand {
378                glyph_cache_pixel_buffer: mesh.glyph_cache_pixel_buffer(),
379                glyph_cpu_buffer_pool: glyph_uploads.clone(),
380                glyph_cache_texture: glyph_cache_tex.clone(),
381            }),
382        };
383        Ok(glyph_cache_cmd)
384    }
385
386    /// Draws using the inner list of `Command`s to a list of `DrawCommand`s compatible with the
387    /// vulkano command buffer builders.
388    ///
389    /// Uses the given `queue` for submitting vertex buffers.
390    ///
391    /// Note: If you require more granular control over rendering, you may want to use the `fill`
392    /// and `commands` methods separately. This method is simply a convenience wrapper around those
393    /// methods for the case that the user does not require accessing or modifying conrod's draw
394    /// parameters, uniforms or generated draw commands.
395    pub fn draw(
396        &mut self,
397        queue: Arc<Queue>,
398        image_map: &image::Map<Image>,
399        viewport: [f32; 4],
400    ) -> Result<Vec<DrawCommand>, DrawError> {
401        let current_viewport = Viewport {
402            origin: [viewport[0], viewport[1]],
403            dimensions: [viewport[2] - viewport[0], viewport[3] - viewport[1]],
404            depth_range: 0.0..1.0,
405        };
406
407        let mut current_scizzor = Scissor {
408            origin: [viewport[0] as u32, viewport[1] as u32],
409            dimensions: [
410                (viewport[2] - viewport[0]) as u32,
411                (viewport[3] - viewport[1]) as u32,
412            ],
413        };
414
415        let conv_scizzor = |s: mesh::Scizzor| Scissor {
416            origin: [s.top_left[0] as u32, s.top_left[1] as u32],
417            dimensions: s.dimensions,
418        };
419        let mut desc_set_builder = self.tex_descs.next();
420        desc_set_builder.add_sampled_image(
421            ImageView::new(self.glyph_cache_tex.clone()).unwrap(),
422            self.sampler.clone(),
423        )?;
424        let desc_cache = Arc::new(desc_set_builder.build()?);
425
426        let commands = self.mesh.commands();
427
428        let mut draw_commands = vec![];
429
430        for command in commands {
431            match command {
432                // Update the `scizzor` before continuing to draw.
433                mesh::Command::Scizzor(scizzor) => current_scizzor = conv_scizzor(scizzor),
434
435                // Draw to the target with the given `draw` command.
436                mesh::Command::Draw(draw) => match draw {
437                    // Draw text and plain 2D geometry.
438                    mesh::Draw::Plain(vert_range) => {
439                        if !vert_range.is_empty() {
440                            let verts = &self.mesh.vertices()[vert_range];
441                            let verts = conv_vertex_buffer(verts);
442                            let (vbuf, vbuf_fut) = ImmutableBuffer::<[Vertex]>::from_iter(
443                                verts.iter().cloned(),
444                                BufferUsage::vertex_buffer(),
445                                queue.clone(),
446                            )?;
447                            vbuf_fut
448                                .then_signal_fence_and_flush()
449                                .expect("failed to flush future")
450                                .wait(None)
451                                .unwrap();
452                            draw_commands.push(DrawCommand {
453                                graphics_pipeline: self.pipeline.clone(),
454                                scissor: current_scizzor,
455                                viewport: current_viewport.clone(),
456                                vertex_buffer: vbuf,
457                                descriptor_set: desc_cache.clone(),
458                            });
459                        }
460                    }
461
462                    // Draw an image whose texture data lies within the `image_map` at the
463                    // given `id`.
464                    mesh::Draw::Image(image_id, vert_range) => {
465                        if vert_range.is_empty() {
466                            continue;
467                        }
468                        if let Some(image) = image_map.get(&image_id) {
469                            let mut desc_set_builder = self.tex_descs.next();
470                            desc_set_builder.add_sampled_image(
471                                ImageView::new(image.image_access.clone()).unwrap(),
472                                self.sampler.clone(),
473                            )?;
474                            let desc_image = Arc::new(desc_set_builder.build()?);
475
476                            let verts = &self.mesh.vertices()[vert_range];
477                            let verts = conv_vertex_buffer(verts);
478                            let (vbuf, vbuf_fut) = ImmutableBuffer::from_iter(
479                                verts.iter().cloned(),
480                                BufferUsage::vertex_buffer(),
481                                queue.clone(),
482                            )?;
483                            vbuf_fut
484                                .then_signal_fence_and_flush()
485                                .unwrap()
486                                .wait(None)
487                                .unwrap();
488                            draw_commands.push(DrawCommand {
489                                graphics_pipeline: self.pipeline.clone(),
490                                scissor: current_scizzor,
491                                viewport: current_viewport.clone(),
492                                vertex_buffer: vbuf,
493                                descriptor_set: desc_image,
494                            });
495                        }
496                    }
497                },
498            }
499        }
500
501        Ok(draw_commands)
502    }
503}
504
505fn conv_vertex_buffer(buffer: &[mesh::Vertex]) -> &[Vertex] {
506    unsafe { &*(buffer as *const [conrod_core::mesh::Vertex] as *const [Vertex]) }
507}
508
509impl From<vulkano::OomError> for RendererCreationError {
510    fn from(err: OomError) -> Self {
511        RendererCreationError::ShaderLoad(err)
512    }
513}
514
515impl From<SamplerCreationError> for RendererCreationError {
516    fn from(err: SamplerCreationError) -> Self {
517        RendererCreationError::SamplerCreation(err)
518    }
519}
520
521impl From<GraphicsPipelineCreationError> for RendererCreationError {
522    fn from(err: GraphicsPipelineCreationError) -> Self {
523        RendererCreationError::GraphicsPipelineCreation(err)
524    }
525}
526
527impl From<ImageCreationError> for RendererCreationError {
528    fn from(err: ImageCreationError) -> Self {
529        RendererCreationError::ImageCreation(err)
530    }
531}
532
533impl From<DescriptorSetError> for DrawError {
534    fn from(err: DescriptorSetError) -> Self {
535        DrawError::DescriptorSet(err)
536    }
537}
538
539impl From<DeviceMemoryAllocError> for DrawError {
540    fn from(err: DeviceMemoryAllocError) -> Self {
541        DrawError::VertexBufferAlloc(err)
542    }
543}
544
545impl StdError for RendererCreationError {
546    fn cause(&self) -> Option<&dyn StdError> {
547        match *self {
548            RendererCreationError::SamplerCreation(ref err) => Some(err),
549            RendererCreationError::ShaderLoad(ref err) => Some(err),
550            RendererCreationError::GraphicsPipelineCreation(ref err) => Some(err),
551            RendererCreationError::ImageCreation(ref err) => Some(err),
552        }
553    }
554}
555
556impl StdError for DrawError {
557    fn cause(&self) -> Option<&dyn StdError> {
558        match *self {
559            DrawError::DescriptorSet(ref err) => Some(err),
560            DrawError::VertexBufferAlloc(ref err) => Some(err),
561        }
562    }
563}
564
565impl fmt::Display for RendererCreationError {
566    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
567        match *self {
568            RendererCreationError::SamplerCreation(ref err) => err.fmt(f),
569            RendererCreationError::ShaderLoad(ref err) => err.fmt(f),
570            RendererCreationError::GraphicsPipelineCreation(ref err) => err.fmt(f),
571            RendererCreationError::ImageCreation(ref err) => err.fmt(f),
572        }
573    }
574}
575
576impl fmt::Display for DrawError {
577    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
578        match *self {
579            DrawError::DescriptorSet(ref err) => err.fmt(f),
580            DrawError::VertexBufferAlloc(ref err) => err.fmt(f),
581        }
582    }
583}